
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
Post new comment