I would like to switch the camera I’m working on, from GL (GPUImage) to Metal.
I tried to figure out what may be wrong with the Metal shader I’m using in order to apply a lookup filter to a MTLtexture, but I have some banding issues, blue lines, especially when displaying dark gradients.
These blue banding issues appear when applying the metal shader, not when using CoreImage.
The rest of the image is just fine.
Here is an example using a lookup image to produce high-contrast:
Left:CoreImage Shader; Right: Metal Shader
(source image is the exactly same for the two examples)
As you can see, Metal seems to be less precise than CoreImage and produces these strange blue bands.
I'd like to know if this is a normal issue, considering both shaders are an adaptation of Brad Larson's GPUImage work.
The Metal Shader:
kernel void LookupFilterKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
texture2d<float, access::read> lutTexture [[texture(2)]],
uint2 gid [[thread_position_in_grid]]){
float4 color = sourceTexture.read(gid);
float blueColor = color.b * 63.0;
float2 quad1;
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
float2 quad2;
quad2.y = floor(ceil(blueColor) / 8.0);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
float2 texPos1;
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float4 newColor1 = lutTexture.read(uint2(texPos1.x * 512 , texPos2.y * 512));
float4 newColor2 = lutTexture.read(uint2(texPos2.x * 512, texPos2.y * 512 ));
float4 newColor = mix(newColor1, newColor2, fract(blueColor));
float4 finalColor = mix(color, float4(newColor.rgb, color.w), 1.0);
outTexture.write(finalColor, gid);
}
And the CoreImage Shader:
kernel vec4 LookupFilterKernel(sampler inputImage, sampler inputLUT, float intensity) {
vec4 textureColor = sample(inputImage,samplerCoord(inputImage));
textureColor = clamp(textureColor, vec4(0.0), vec4(1.0));
float blueColor = textureColor.b * 63.0;
vec2 quad1;
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
vec2 quad2;
quad2.y = floor(ceil(blueColor) / 8.0);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
vec2 texPos1;
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
vec2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
texPos1.y = 1.0 - texPos1.y;
texPos2.y = 1.0 - texPos2.y;
vec4 inputLUTExtent = samplerExtent(inputLUT);
vec4 newColor1 = sample(inputLUT, samplerTransform(inputLUT, texPos1 * vec2(512.0) + inputLUTExtent.xy));
vec4 newColor2 = sample(inputLUT, samplerTransform(inputLUT, texPos2 * vec2(512.0) + inputLUTExtent.xy));
vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
return mix(textureColor, vec4(newColor.rgb, textureColor.a), intensity);
}
The source texture is a MTLTexture im receiving from the camera's captureOutput function.
If you have any idea why I have this problem or if you need more code / explanations, please let me know.
Many thanks !
EDIT 1:
Ok so after Frank's answer, I tried to fix these gamma issues. So I linearized the LUT texture and the source texture, updating the shader:
static float linearTosRGB(float c) {
return powr(c, 1./2.2);
}
static float srgbToLinear(float c) {
if (c <= 0.04045)
return c / 12.92;
else
return powr((c + 0.055) / 1.055, 2.4);
}
kernel void LookupFilterKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
texture2d<float, access::read> lutTexture [[texture(2)]],
uint2 gid [[thread_position_in_grid]]){
float4 color = sourceTexture.read(gid);
color.r = srgbToLinear(color.r);
color.g = srgbToLinear(color.g);
color.b = srgbToLinear(color.b);
float blueColor = color.b * 63.0;
float2 quad1;
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
float2 quad2;
quad2.y = floor(ceil(blueColor) / 8.0);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
float2 texPos1;
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float4 newColor1 = lutTexture.read(uint2(texPos1.x * 512 , texPos2.y * 512));
float4 newColor2 = lutTexture.read(uint2(texPos2.x * 512, texPos2.y * 512 ));
float4 newColor = mix(newColor1, newColor2, fract(blueColor));
float4 finalColor = mix(color, float4(newColor.rgb, color.w), 1.0);
finalColor.r = linearTosRGB(finalColor.r);
finalColor.g = linearTosRGB(finalColor.g);
finalColor.b = linearTosRGB(finalColor.b);
outTexture.write(finalColor, gid);
}
The LUT texture is previously transformed into linear gamma with this shader:
kernel void sRGBToLinearKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
uint2 gid [[thread_position_in_grid]]){
float4 color = sourceTexture.read(gid);
color.r = srgbToLinear(color.r);
color.g = srgbToLinear(color.g);
color.b = srgbToLinear(color.b);
outTexture.write(color, gid);
}
And ... it didn't fixed anything. I still have this strange banding. Maybe i'm doing something wrong ?
EDIT 2:
I tried to put the pixel format to MTLPixelFormatRGBA16Float, but result is still wrong.
I realized that if I changed this line in the shader:
float4 newColor = mix(newColor1, newColor2, fract(blueColor));
to:
float4 newColor = mix(newColor1, newColor2, 1.0);
the blue bands disappear. I'm assuming that the calculation of quad1 isn't right and produces these bands.
So my question is: why this occurs only on metal shader and not in CoreImage or GPUImage's ones ?
Related
I want to port a example from Shadertoy to the iPhone with GLKit(Here).However, the effect is not very good when the program runs. The rendering effect is not smooth. Even the time interval was almost four seconds. I suspect it's because of the complexity of the operation in fragment shader. So I was wondering if there was a way to render without getting stuck, thank you.
fragment shader:
precision highp float;
varying highp vec2 TextureCoordsVarying;
uniform vec2 SceneSize;
uniform float elapsedTime;
#define TAU 6.2831852
#define MOD3 vec3(.1031,.11369,.13787)
#define BLACK_COL vec3(16,21,25)/255.
vec3 hash33(vec3 p3)
{
p3 = fract(p3 * MOD3);
p3 += dot(p3, p3.yxz+19.19);
return -1.0 + 2.0 * fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
}
float simplex_noise(vec3 p)
{
const float K1 = 0.333333333;
const float K2 = 0.166666667;
vec3 i = floor(p + (p.x + p.y + p.z) * K1);
vec3 d0 = p - (i - (i.x + i.y + i.z) * K2);
vec3 e = step(vec3(0.0), d0 - d0.yzx);
vec3 i1 = e * (1.0 - e.zxy);
vec3 i2 = 1.0 - e.zxy * (1.0 - e);
vec3 d1 = d0 - (i1 - 1.0 * K2);
vec3 d2 = d0 - (i2 - 2.0 * K2);
vec3 d3 = d0 - (1.0 - 3.0 * K2);
vec4 h = max(0.6 - vec4(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), 0.0);
vec4 n = h * h * h * h * vec4(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.0)));
return dot(vec4(31.316), n);
}
void main() {
vec2 uv = 2.0 * TextureCoordsVarying.xy - 1.0;
if (SceneSize.x > SceneSize.y) {
uv.x *= SceneSize.x/SceneSize.y;
}else{
uv.y *= SceneSize.y/SceneSize.x;
}
float a = sin(atan(uv.y, uv.x));
float am = abs(a-.5)/4.;
float l = length(uv);
float t = elapsedTime;
float m1 = clamp(.1/smoothstep(.0, 1.75, l), 0., 1.);
float m2 = clamp(.1/smoothstep(.42, 0., l), 0., 1.);
float s1 = (simplex_noise(vec3(uv*2., 1. + t*.525))*(max(1.0 - l*1.75, 0.)) + .9);
float s2 = (simplex_noise(vec3(uv*1., 15. + t*.525))*(max(.0 + l*1., .025)) + 1.25);
float s3 = (simplex_noise(vec3(vec2(am, am*100. + t*3.)*.15, 30. + t*.525))*(max(.0 + l*1., .25)) + 1.5);
s3 *= smoothstep(0.0, .3345, l);
float sh = smoothstep(0.15, .35, l);
float m = m1*m1*m2 * ((s1*s2*s3) * (1.-l)) * sh;
//m = clamp(m, 0., 1.);
vec3 col = mix(BLACK_COL, (0.5 + 0.5*cos(t+uv.xyx*3.+vec3(0,2,4))), m);
gl_FragColor = vec4(col, 1.);
}
Fragment Shader
vertex VertexOutBezier bezier_vertex(constant BezierParameters *allParams[[buffer(0)]],
// constant GlobalParameters& globalParams[[buffer(1)]],
uint vertexId [[vertex_id]],
uint instanceId [[instance_id]])
{
float t = (float) vertexId / 300;
rint(t);
BezierParameters params = allParams[instanceId];
float lineWidth = (1 - (((float) (vertexId % 2)) * 2.0)) * params.lineThickness;
float2 a = params.a;
float2 b = params.b;
float nt = 1.0f - t;
float nt_2 = nt * nt;
float nt_3 = nt_2 * nt;
float t_2 = t * t;
float t_3 = t_2 * t;
float2 point = a * nt_3 + params.p1 * nt_2 * t + params.p2 * nt * t_2 + b * t_3;
float2 tangent = -3.0 * a * nt_2 + params.p1 * (1.0 - 4.0 * t + 3.0 * t_2) + params.p2 * (2.0 * t - 3.0 * t_2) + 3 * b * t_2;
tangent = normalize(float2(-tangent.y, tangent.x));
VertexOutBezier vo;
vo.pos.xy = point + (tangent * (lineWidth / 3.0f));
vo.pos.zw = float2(0, 1);
vo.color = params.color ;
return vo;
}
My Fragment shader is
fragment float4 bezier_fragment(VertexOutBezier params[[stage_in]],
texture2d<float> texture [[texture(0)]]
)
{
constexpr sampler defaultSampler;
float4 canvasColor = texture.sample(defaultSampler, params.pos.xy);
return canvasColor;
}
Here i expect to get the pixel color of the texture. But here it is only getting single color. It is not getting the color of the texture according to its position.
Even when I do this in fragment I am getting the single color it is not varying with coordinates
fragment float4 bezier_fragment(VertexOutBezier params[[stage_in]],
texture2d<float> texture [[texture(0)]]
)
{
constexpr sampler defaultSampler;
float4 canvasColor = params.color * params.pos.x;
return canvasColor;
}
If I do this in Vertex Shader I got color varying according position of x
vo.pos.xy = point + (tangent * (lineWidth / 3.0f));
vo.pos.zw = float2(0, 1);
vo.color = params.color * vo.pos.x;
What is the Issue in fragment Shader. I cannot get the coordinates from Vertex Shader
Please make sure the VertexOutBezier.pos.xy value is normalization ( 0 ~ 1.0) ,due to the defaultSampler only receive normalization position value, if always return a single may be the position is beyond the bounds.
I'm trying to convert this particular shader to CIKernel Code.
https://www.shadertoy.com/view/4scBRH
I've got this soo far,
kernel vec4 thresholdFilter(__sample image, float time)
{
vec2 uv = destCoord();
float amount = sin(time) * 0.1;
amount *= 0.3;
float split = 1. - fract(time / 2.0);
float scanOffset = 0.01;
vec2 uv1 = vec2(uv.x + amount, uv.y);
vec2 uv2 = vec2(uv.x, uv.y + amount);
if (uv.y > split) {
uv.x += scanOffset;
uv1.x += scanOffset;
uv2.x += scanOffset;
}
float r = sample(image, uv1).r;
float g = sample(image, uv).g;
float b = sample(image, uv2).b;
float a = 1.0;
vec3 outPutPixel = sample(image, samplerTransform(image, uv)).rgb;
return vec4(outPutPixel, 1.0);
}
The output of this code is not even close to the shaderToy output.
Working on another OpenGL ES image filter based on this:
uniform sampler2D texture;
uniform float amount;
uniform vec2 texSize;
varying vec2 texCoord;
void main() {
vec4 color = texture2D(texture, texCoord);
vec4 orig = color;
/* High pass filter */
vec4 highpass = color * 5.0;
float dx = 1.0 / texSize.x;
float dy = 1.0 / texSize.y;
highpass += texture2D(texture, texCoord + vec2(-dx, -dy)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(dx, -dy)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(dx, dy)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(-dx, dy)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(-dx * 2.0, -dy * 2.0)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(dx * 2.0, -dy * 2.0)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(dx * 2.0, dy * 2.0)) * -0.625;
highpass += texture2D(texture, texCoord + vec2(-dx * 2.0, dy * 2.0)) * -0.625;
highpass.a = 1.0;
/* Overlay blend */
vec3 overlay = vec3(1.0);
if (highpass.r <= 0.5) {
overlay.r = 2.0 * color.r * highpass.r;
} else {
overlay.r = 1.0 - 2.0 * (1.0 - color.r) * (1.0 - highpass.r);
}
if (highpass.g <= 0.5) {
overlay.g = 2.0 * color.g * highpass.g;
} else {
overlay.g = 1.0 - 2.0 * (1.0 - color.g) * (1.0 - highpass.g);
}
if (highpass.b <= 0.5) {
overlay.b = 2.0 * color.b * highpass.b;
} else {
overlay.b = 1.0 - 2.0 * (1.0 - color.b) * (1.0 - highpass.b);
}
color.rgb = (overlay * 0.8) + (orig.rgb * 0.2);
/* Desaturated hard light */
vec3 desaturated = vec3(orig.r + orig.g + orig.b / 3.0);
if (desaturated.r <= 0.5) {
color.rgb = 2.0 * color.rgb * desaturated;
} else {
color.rgb = vec3(1.0) - vec3(2.0) * (vec3(1.0) - color.rgb) * (vec3(1.0) - desaturated);
}
color = (orig * 0.6) + (color * 0.4);
/* Add back some color */
float average = (color.r + color.g + color.b) / 3.0;
color.rgb += (average - color.rgb) * (1.0 - 1.0 / (1.001 - 0.45));
gl_FragColor = (color * amount) + (orig * (1.0 - amount));
}
Per my question yesterday, I knew to assign precision to each float and vec. This time it compiled fine, however when I go to apply the filter in GPUImage (e.g. by setting the value of clarity to 0.8), the image goes black. My gut tells me this is related to the texture size, but without knowing how GPUImage handles that, I'm kinda stuck.
Here's my implementation in Objective-C:
.h
#import <GPUImage/GPUImage.h>
#interface GPUImageClarityFilter : GPUImageFilter
{
GLint clarityUniform;
}
// Gives the image a gritty, surreal contrasty effect
// Value 0 to 1
#property (readwrite, nonatomic) GLfloat clarity;
#end
.m
#import "GPUImageClarityFilter.h"
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kGPUImageClarityFragmentShaderString = SHADER_STRING
(
uniform sampler2D inputImageTexture;
uniform lowp float clarity;
uniform highp vec2 textureSize;
varying highp vec2 textureCoordinate;
void main() {
highp vec4 color = texture2D(inputImageTexture, textureCoordinate);
highp vec4 orig = color;
/* High pass filter */
highp vec4 highpass = color * 5.0;
highp float dx = 1.0 / textureSize.x;
highp float dy = 1.0 / textureSize.y;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx, -dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx, -dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx, dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx, dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx * 2.0, -dy * 2.0)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx * 2.0, -dy * 2.0)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx * 2.0, dy * 2.0)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx * 2.0, dy * 2.0)) * -0.625;
highpass.a = 1.0;
/* Overlay blend */
highp vec3 overlay = vec3(1.0);
if (highpass.r <= 0.5) {
overlay.r = 2.0 * color.r * highpass.r;
} else {
overlay.r = 1.0 - 2.0 * (1.0 - color.r) * (1.0 - highpass.r);
}
if (highpass.g <= 0.5) {
overlay.g = 2.0 * color.g * highpass.g;
} else {
overlay.g = 1.0 - 2.0 * (1.0 - color.g) * (1.0 - highpass.g);
}
if (highpass.b <= 0.5) {
overlay.b = 2.0 * color.b * highpass.b;
} else {
overlay.b = 1.0 - 2.0 * (1.0 - color.b) * (1.0 - highpass.b);
}
color.rgb = (overlay * 0.8) + (orig.rgb * 0.2);
/* Desaturated hard light */
highp vec3 desaturated = vec3(orig.r + orig.g + orig.b / 3.0);
if (desaturated.r <= 0.5) {
color.rgb = 2.0 * color.rgb * desaturated;
} else {
color.rgb = vec3(1.0) - vec3(2.0) * (vec3(1.0) - color.rgb) * (vec3(1.0) - desaturated);
}
color = (orig * 0.6) + (color * 0.4);
/* Add back some color */
highp float average = (color.r + color.g + color.b) / 3.0;
color.rgb += (average - color.rgb) * (1.0 - 1.0 / (1.001 - 0.45));
gl_FragColor = (color * clarity) + (orig * (1.0 - clarity));
}
);
#else
NSString *const kGPUImageClarityFragmentShaderString = SHADER_STRING
(
uniform sampler2D inputImageTexture;
uniform float clarity;
uniform vec2 textureSize;
varying vec2 textureCoordinate;
void main() {
vec4 color = texture2D(inputImageTexture, textureCoordinate);
vec4 orig = color;
/* High pass filter */
vec4 highpass = color * 5.0;
float dx = 1.0 / textureSize.x;
float dy = 1.0 / textureSize.y;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx, -dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx, -dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx, dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx, dy)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx * 2.0, -dy * 2.0)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx * 2.0, -dy * 2.0)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(dx * 2.0, dy * 2.0)) * -0.625;
highpass += texture2D(inputImageTexture, textureCoordinate + vec2(-dx * 2.0, dy * 2.0)) * -0.625;
highpass.a = 1.0;
/* Overlay blend */
vec3 overlay = vec3(1.0);
if (highpass.r <= 0.5) {
overlay.r = 2.0 * color.r * highpass.r;
} else {
overlay.r = 1.0 - 2.0 * (1.0 - color.r) * (1.0 - highpass.r);
}
if (highpass.g <= 0.5) {
overlay.g = 2.0 * color.g * highpass.g;
} else {
overlay.g = 1.0 - 2.0 * (1.0 - color.g) * (1.0 - highpass.g);
}
if (highpass.b <= 0.5) {
overlay.b = 2.0 * color.b * highpass.b;
} else {
overlay.b = 1.0 - 2.0 * (1.0 - color.b) * (1.0 - highpass.b);
}
color.rgb = (overlay * 0.8) + (orig.rgb * 0.2);
/* Desaturated hard light */
vec3 desaturated = vec3(orig.r + orig.g + orig.b / 3.0);
if (desaturated.r <= 0.5) {
color.rgb = 2.0 * color.rgb * desaturated;
} else {
color.rgb = vec3(1.0) - vec3(2.0) * (vec3(1.0) - color.rgb) * (vec3(1.0) - desaturated);
}
color = (orig * 0.6) + (color * 0.4);
/* Add back some color */
float average = (color.r + color.g + color.b) / 3.0;
color.rgb += (average - color.rgb) * (1.0 - 1.0 / (1.001 - 0.45));
gl_FragColor = (color * clarity) + (orig * (1.0 - clarity));
}
);
#endif
#implementation GPUImageClarityFilter
#synthesize clarity = _clarity;
#pragma mark -
#pragma mark Initialization and teardown
- (id)init;
{
if (!(self = [super initWithFragmentShaderFromString:kGPUImageClarityFragmentShaderString]))
{
return nil;
}
clarityUniform = [filterProgram uniformIndex:#"clarity"];
self.clarity = 0.0;
return self;
}
#pragma mark -
#pragma mark Accessors
- (void)setClarity:(GLfloat)clarity;
{
_clarity = clarity;
[self setFloat:_clarity forUniform:clarityUniform program:filterProgram];
}
#end
One other thing I thought of doing is applying GPUImage's built-in low pass and high pass filters, but I get the feeling that would end up a rather clunky solution.
That's probably due to textureSize not being a standard uniform that is provided for you as part of a GPUImageFilter. inputImageTexture and textureCoordinate are standard uniforms provided by one of these filters, and it looks like you're providing the clarity uniform.
Since textureSize isn't set, it will default to 0.0. Your 1.0 / textureSize.x calculation will then divide by zero, which tends to lead to black frames in an iOS fragment shader.
You could either calculate and provide that uniform, or instead take a look at basing your custom filter on GPUImage3x3TextureSamplingFilter instead. That filter base class passes in the result of 1.0 / textureSize.x as the texelWidth uniform (and the matching texelHeight for the vertical component). You don't have to calculate this. In fact, it also calculates the texture coordinates of the surrounding 8 pixels, so you can cut out four of the calculations above and convert those to non-dependent texture reads. You'd just need to calculate the four texture reads based on 2 * texelWidth and 2 * texelHeight to finish off the remaining four reads.
You may in fact be able to break this operation into multiple passes to save on calculations, doing a small box blur, then an overlay blend, then the last stage of this filter. That could speed this up further.
So, you can override
(void)setupFilterForSize:(CGSize)filterFrameSize
Method to setup width & height factor like GPUImageSharpenFilter.
I know to perform this in OpenGL with the code below:
glDisable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
how to simulate that in WebGL?
Here is a blinn-phong shader which emulates the OpenGL fixed function pipeline for per vertex lighting. By default this is equivalent to having glEnable(GL_COLOR_MATERIAL) and glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE) enabled. You can emulate glColorMaterial by setting the uniforms to 1.0 which will cause material parameters track the current color instead of those set by glMaterial.
#version 120
////////////////////////////////////////////////////////////////////////////////
// http://www.glprogramming.com/red/chapter05.html //
// //
// color = (matEmission + globalAmbient * matAmbient) + //
// AttenuationFactor( 1.0 / ( Kc + Kl*d + Kq*d^2 ) ) * //
// [ (lightAmbient * matAmbient) + //
// (max(N.L,0) * lightDiffuse * matDiffuse) + //
// (max(N.H,0)^matShininess * lightSpecular * matSpecular) ] //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Uniforms //
////////////////////////////////////////////////////////////////////////////////
uniform float uColorMaterialAmbient = 1.0;
uniform float uColorMaterialDiffuse = 1.0;
uniform float uColorMaterialEmission = 0.0;
uniform float uColorMaterialSpecular = 0.0;
////////////////////////////////////////////////////////////////////////////////
// Main //
////////////////////////////////////////////////////////////////////////////////
void main(void)
{
vec4 matAmbient = mix(gl_FrontMaterial.ambient, gl_Color, uColorMaterialAmbient);
vec4 matDiffuse = mix(gl_FrontMaterial.diffuse, gl_Color, uColorMaterialDiffuse);
vec4 matEmission = mix(gl_FrontMaterial.emission, gl_Color, uColorMaterialEmission);
vec4 matSpecular = mix(gl_FrontMaterial.specular, gl_Color, uColorMaterialSpecular);
// Transform normal into eye space. gl_NormalMatrix is the transpose of the
// inverse of the upper leftmost 3x3 of gl_ModelViewMatrix.
vec3 eyeNormal = normalize(gl_NormalMatrix * gl_Normal);
// Calculate emission and global ambient light
vec4 emissionAmbient = matEmission + (gl_LightModel.ambient * matAmbient);
// Calculate ambient
vec4 lightAmbient = gl_LightSource[0].ambient * matAmbient;
// Transform the vertex into eye space
vec4 eyeVertex = gl_ModelViewMatrix * gl_Vertex;
vec3 eyeLightDir = gl_LightSource[0].position.xyz - eyeVertex.xyz;
float dist = length(eyeLightDir);
eyeLightDir = normalize(eyeLightDir);
// No attenuation for a directional light
float attenuationFactor = 1.0 / (gl_LightSource[0].constantAttenuation
+ gl_LightSource[0].linearAttenuation * dist
+ gl_LightSource[0].quadraticAttenuation * dist * dist);
// Calculate lambert term
float NdotL = max(dot(eyeNormal, eyeLightDir), 0.0);
// Calculate diffuse
vec4 lightDiffuse = NdotL * (gl_LightSource[0].diffuse * matDiffuse);
// Calculate specular
vec4 lightSpecular = vec4(0.0);
if ( NdotL > 0.0 )
{
float NdotHV = max(dot(eyeNormal, gl_LightSource[0].halfVector.xyz), 0.0);
lightSpecular = pow(NdotHV, gl_FrontMaterial.shininess) * (gl_LightSource[0].specular * matSpecular);
}
gl_FrontColor = emissionAmbient + attenuationFactor * (lightAmbient + lightDiffuse + lightSpecular);
gl_Position = ftransform();
}