Shader特效——"Floyd Steinberg 抖动” 的实现

转自:http://blog.csdn.net/panda1234lee/article/details/52245353

OpenCV的效果图:

用OpenCV实现起来也是非常简单的!

intmain(intargc,char** argv )

{

Mat rawImg;

Mat dithImg;

intimgWidth;

intimgHeight;

uint8_t** imgPtr;

if(argc != 2)

{

printf("> Usage: ./dithering [path_to_image]\n");

return-1;

}

rawImg = imread(argv[1]);

cvtColor(rawImg, dithImg, CV_BGR2GRAY);

/* 获得图像尺寸 */

imgWidth = dithImg.cols;

imgHeight = dithImg.rows;

dbg("> Image width: %d\r\n",imgWidth);

dbg("> Image height: %d\r\n",imgHeight);

/* Floyd-Steinberg 抖动算法 */

interr;

int8_t a,b,c,d;

for(inti=0; i

{

for(intj=0; j

{

if(dithImg.at(i,j) > 127)// 127 = (255+0)/2

{

err = dithImg.at(i,j) - 255;

dithImg.at(i,j) = 255;// 新值为白色

}

else

{

err = dithImg.at(i,j) - 0;

dithImg.at(i,j) = 0;// 新值为黑色

}

// 误差传递

a = (err * 7) / 16;

b = (err * 1) / 16;

c = (err * 5) / 16;

d = (err * 3) / 16;

if((i != (imgHeight-1)) && (j != 0) && (j != (imgWidth - 1)))

{

dithImg.at(i+0,j+1) = saturated_add(dithImg.at(i+0,j+1),a);

dithImg.at(i+1,j+1) = saturated_add(dithImg.at(i+1,j+1),b);

dithImg.at(i+1,j+0) = saturated_add(dithImg.at(i+1,j+0),c);

dithImg.at(i+1,j-1) = saturated_add(dithImg.at(i+1,j-1),d);

}

}

}

/* Show results. */

imshow("Raw Image",rawImg);

imshow("Dithered Image",dithImg);

printf("> Press any key to exit ...\n");

waitKey(0);

return0;

}

但是这么简单的算法用GLSL来写的话,就没有这么简单了!因为GLSL是并行的,决定当前像素的 error 不仅仅来自于vec2(-1., -1.), vec2(0., -1.), vec2(-1., 0.), vec2(1., -1.)这四个 offset 方向上的像素,这四个像素还受其本身四个方向 offset 的像素影响,所以会陷入“递归”的问题,而GLSL又不支持递归。那么只能换一种思路来近似抖动效果了。

GLSL代码:

[cpp]view plaincopy

#ifdef GL_ES

precision mediumpfloat;

precision mediumpint;

#endif

uniform sampler2D texture;

vec2 sketchSize = vec2(512., 512.);

constintlookupSize = 64;// 查找表的大小

constfloaterrorCarry = 0.5;// 误差传输率

floatgetGrayscale(vec2 coords)

{

vec2 uv = coords / sketchSize.xy;

// processing is already using inverted y coordinates

// uv.y = 1.0-uv.y;

vec3 sourcePixel = texture2D(texture, uv).rgb;

returnlength(sourcePixel * vec3(0.2126,0.7152,0.0722));

}

voidmain()

{

floatxError = 0.0;

for(intxLook=0; xLook

{

floatgrayscale = getGrayscale(gl_FragCoord.xy + vec2(-lookupSize+xLook,0));//

grayscale += xError;// 在X方向上的误差传播(从最左端到当前像素)

floatbit = (grayscale >= 0.5) ? 1.0 : 0.0;// t = (w+b)/2.

xError = (grayscale - bit)*errorCarry;

}

floatyError = 0.0;

for(intyLook=0; yLook

{

floatgrayscale = getGrayscale(gl_FragCoord.xy + vec2(0,-lookupSize+yLook));

grayscale += yError;// 在Y方向上的误差传播 (从最上端到当前像素)

floatbit = (grayscale >= 0.5) ? 1.0 : 0.0;// t = (w+b)/2.

yError = (grayscale - bit)*errorCarry;

}

floatfinalGrayscale = getGrayscale(gl_FragCoord.xy);

finalGrayscale += xError * .5;// X,Y方向的误差各占一半

finalGrayscale += yError * .5;

finalGrayscale = clamp(finalGrayscale, 0., 1.);

floatfinalBit = finalGrayscale >= 0.5 ? 1.0 : 0.0;

gl_FragColor = vec4(finalBit, finalBit, finalBit, 1.);

}

voidmain(void) {

vec2 uv = gl_FragCoord.xy / sketchSize.xy;

uv.y = uv.y;

vec3 sourcePixel = texture2D(texture, uv).rgb;

floatgrayscale = length(sourcePixel*vec3(0.2126,0.7152,0.0722));

vec3 ditherPixel = texture2D(noiseTexture, vec2(fract(gl_FragCoord.xy/noiseSketchSize))).xyz;

floatditherGrayscale = (ditherPixel.x + ditherPixel.y + ditherPixel.z) / 3.0;

ditherGrayscale -= 0.6;

floatditheredResult = grayscale + ditherGrayscale;

floatbit = ditheredResult >= 0.5 ? 1.0 : 0.0;

gl_FragColor = vec4(bit,bit,bit,1);

}


效果如图

显然效果会更接近于正统的Floyd Steinberg,而且计算量也较小,噪声纹理。

你可能感兴趣的:(Shader特效——"Floyd Steinberg 抖动” 的实现)