
I've started experimenting with the awesome Pixel Bender Toolkit (a sort-of shader for 2D images) and since I'm obsessed with HDR-esque imagery, well my first 'hello-world' shader I decided to write would be one that gives pictures an HDR-like highlight effect
. The result is far from perfect, there are noticeable artifacts, but it is reasonable. The preview comparison image shows the result as-is, without any post-processing (except the logo and the split, of course).
Kernel implementation
For each pixel, sample neighbouring pixels and compute their 'brightness' (in this case, average of R, G, B values). Bright pixels are further brightened, whereas darker pixels are suppressed (using combination of bright and acctweak parameters). The outputted pixel then is
output pixel = original pixel + accumulated contribution of neighbouring pixels * weight
The contribution is further affected by the brightness of the original pixel itself. For simplicity, the sampled pixels are placed in a grid.
Parameters
There are several parameters used for tweaking. Not all of them have sensible names, I'll work on it in the future, but for now:- factor: overall effect on the image. 0 means no effect. You can go over 1.0 for extra brightness.
- power: brightness contribution from the pixel itself. Use to brighten up the whole image.
- rings: number of sampling 'rings' to accumulate pixels from
- radius: separation of sampling 'rings' from each other in pixels
- bright: scales the luminance on accumulated pixels. Higher means brighter
- acctweak: parameter for tweaking the contribution of accumulated pixels. Lower values spread brightness across the image, whereas higher values give localised bright areas.
The code
Due to the lack of support for loops in the language, any loops better be expanded. The shader is also available for download at the end of this page.<languageVersion : 1.0;>
kernel Brighten
< namespace : "micron_developers";
vendor : "micron_developers";
version : 1;
description : "Brights up an image based on high luminance pixels";
>
{
parameter float factor <
minValue: float(0.0);
maxValue: float(2.0);
defaultValue: float(0.85);
>;
parameter float bright <
minValue: float(1.0);
maxValue: float(4.0);
defaultValue: float(1.5);
>;
parameter float power <
minValue: float(0.0);
maxValue: float(5.0);
defaultValue: float(4.4);
>;
parameter float acctweak <
minValue: float(0.0);
maxValue: float(5.0);
defaultValue: float(1.3);
>;
parameter int rings <
minValue: int(1);
maxValue: int(5);
defaultValue: int(4);
>;
parameter float radius <
minValue: float(1.0);
maxValue: float(15.0);
defaultValue: float(3.0);
>;
// ring weights
dependent float w1, w2, w3, w4, w5;
input image4 src;
output pixel4 dst;
void evaluateDependents() {
w1 = 10.1;
w2 = 6.75;
w3 = 4.5;
w4 = 3.0;
w5 = 2.0;
}
// customizable "luminance" function
float lum(float4 s) {
return (s.r + s.g + s.b)/3.0;
}
// evaluates the accumulation contribution
float4 evalAcc(float4 s) {
float l = lum(s);
return s*pow(l*bright, acctweak);
}
// evaluatePixel
void evaluatePixel()
{
float4 s = sampleNearest(src, outCoord());
float h = lum(s);
// find contribution from other pixels
float4 acc = float4(0.0);
float4 acc1 = float4(0.0), acc2 = float4(0.0), acc3 = float4(0.0), acc4 = float4(0.0), acc5 = float4(0.0);
float w = 0.00001;
if (rings > 0) {
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-1.0, -1.0)));
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-1.0, 0.0))) * 2.0;
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-1.0, 1.0)));
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, -1.0))) * 2.0;
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, 1.0))) * 2.0;
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(1.0, -1.0)));
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(1.0, 0.0))) * 2.0;
acc1 += evalAcc(sampleNearest(src, outCoord() + radius*float2(1.0, 1.0)));
acc1 /= 12.0;
acc1 *= w1;
w += w1;
}
if (rings > 1) {
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-2.0, -2.0)));
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-2.0, 0.0))) * 2.0;
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-2.0, 2.0)));
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, -2.0))) * 2.0;
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, 2.0))) * 2.0;
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(2.0, -2.0)));
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(2.0, 0.0))) * 2.0;
acc2 += evalAcc(sampleNearest(src, outCoord() + radius*float2(2.0, 2.0)));
acc2 /= 12.0;
acc2 *= w2;
w += w2;
}
if (rings > 2) {
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-3.0, -3.0)));
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-3.0, 0.0))) * 2.0;
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-3.0, 3.0)));
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, -3.0))) * 2.0;
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, 3.0))) * 2.0;
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(3.0, -3.0)));
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(3.0, 0.0))) * 2.0;
acc3 += evalAcc(sampleNearest(src, outCoord() + radius*float2(3.0, 3.0)));
acc3 /= 12.0;
acc3 *= w3;
w += w3;
}
if (rings > 3) {
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-4.0, -4.0)));
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-4.0, 0.0))) * 2.0;
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-4.0, 4.0)));
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, -4.0))) * 2.0;
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, 4.0))) * 2.0;
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(4.0, -4.0)));
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(4.0, 0.0))) * 2.0;
acc4 += evalAcc(sampleNearest(src, outCoord() + radius*float2(4.0, 4.0)));
acc4 /= 12.0;
acc4 *= w4;
w += w4;
}
if (rings > 4) {
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-5.0, -5.0)));
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-5.0, 0.0))) * 2.0;
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(-5.0, 5.0)));
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, -5.0))) * 2.0;
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(0.0, 5.0))) * 2.0;
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(5.0, -5.0)));
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(5.0, 0.0))) * 2.0;
acc5 += evalAcc(sampleNearest(src, outCoord() + radius*float2(5.0, 5.0)));
acc5 /= 12.0;
acc5 *= w5;
w += w5;
}
// aggregate acc
acc = (acc1+acc2+acc3+acc4+acc5)/w;
// account pixel luminance
acc *= pow(h, 5.0-power);
// overall factor
acc *= factor;
// add
dst = s+acc;
}
}
| Attachment | Size |
|---|---|
| brighten.pbk | 5.95 KB |





Comments
Whenever I have free time I try to read either periodical or blog articles (it is possible to find much interesting with the help of http://www.pdfqueen.com pdf search engine). Thus, thank you for your being here and for your great posts.
exactly what I was searching for! thanks
that post is just great, thanks for sharing! I really enjoyed it
Post new comment