iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 23
2
Modern Web

寫給工程師的 WebGL 學習心得系列 第 23

[WebGL - Day23] Ascii Filter 與實作

ViiSUALiiZER
今天討論的是 Ascii Filter 與實作


ViiSUALiiZER
ViiSUALiiZER

前陣子看朋友在討論這個案例,
覺得 Ascii Filter 效果很適合在這個系列挑篇文章討論

因此將 Ascii Filter 分做 Canvas 原理WebGL 原理 兩部分討論
並調整 Shadertoy 裡 Ascii Filter 的程式碼

PixiJS 有相同濾鏡,調整方式也幾乎相同


Ascii Filter 的原理

  • 收集區域範圍內所有點的顏色,並依據顏色分佈密度給予不同圖樣

Canvas 版本:

實作方式因人而異,但大多會有類似的程式碼:

  • 使用雙重迴圈查找顏色
var oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data;
var strChars = "";
for ( var y = 0; y < iHeight; y += 2 ) {
	for ( var x = 0; x < iWidth; x ++ ) {

		var iOffset = ( y * iWidth + x ) * 4;

		var iRed = oImgData[ iOffset ];
		var iGreen = oImgData[ iOffset + 1 ];
		var iBlue = oImgData[ iOffset + 2 ];
		var iAlpha = oImgData[ iOffset + 3 ];
		var fBrightness;
		fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255;
		if ( iAlpha == 0 ) {
			fBrightness = 1;
		}
	}
}

canvas 版本的 Ascii 濾鏡會使用 HTML canvas getImageData() Method
抓取特定範圍內 4 個色版的顏色

由於 getImageData() 回傳的 ImageData 包含4個色版的顏色,
因此可以計算區域內的圖像填滿的程度

red=imgData.data[0];
green=imgData.data[1];
blue=imgData.data[2];
alpha=imgData.data[3];

three.js 的 three.js - ascii filter example 範例,也是使用 Canvas 判斷
濾鏡原始檔: three.js ascii effect

three.js - ascii filter example


WebGL 版本:

也是抓區域裡 RGB 的值做運算:

vec2 pix = fragCoord.xy;
vec3 col = texture(iChannel0, floor(pix/8.0)*8.0/iResolution.xy).rgb;	

float gray = 0.3 * col.r + 0.59 * col.g + 0.11 * col.b;

float n =  4096;               // .
if (gray > 0.2) n = 65600.;    // :
if (gray > 0.3) n = 332772.;   // *
//…

寫法看起來不太相同,原理幾乎相同


Shadertoy 上實作的 Ascii 效果:

Shadertoy - Ascii Art

原始影片:

  • 如先前取圖片的方式,將 iChannel0 設定為影片
vec2 uv = fragCoord/iResolution.xy;
vec3 col = texture(iChannel0, uv).rgb;
fragColor = vec4(col,1.0);

輸出:
origin-video

依據顏色密度,放入不同符號

float n =  4096;               // .
if (gray > 0.2) n = 65600.;    // :
if (gray > 0.3) n = 332772.;   // *
if (gray > 0.4) n = 15255086.; // o 
if (gray > 0.5) n = 23385164.; // &
if (gray > 0.6) n = 15252014.; // 8
if (gray > 0.7) n = 13199452.; // @
if (gray > 0.8) n = 11512810.; // #

顏色分佈越鬆散的,放入的符號越單純 (如.)
顏色分佈越密集的,放入的符號越複雜 (如#)
origin-filter

改寫成與 ViiSUALiiZER 網站裡類似的效果!

ViiSUALiiZER 網站裡,使用的是 i, 符號來排列:
symbol-i

設定成符號 i,

小工具 ASCII tool
可以用勾選的方式把想要的文字 出來成 int 使用:

tool
點出想要的 i, 形狀,輸出的 int 25436541 接下來使用

輸入25436541 時,輸出的 i會上下相反
於是點了個上下相反的i,取得 int 24971331

在 Shadertoy 裡將原本 float n 的部分註解掉,
改成 gray > 0.5 時輸出 i, (n=24971331),其他不輸出:

// float n =  4096;               // .
// if (gray > 0.2) n = 65600.;    // :
// if (gray > 0.3) n = 332772.;   // *
// if (gray > 0.4) n = 15255086.; // o 
// if (gray > 0.5) n = 23385164.; // &
// if (gray > 0.6) n = 15252014.; // 8
// if (gray > 0.7) n = 13199452.; // @
// if (gray > 0.8) n = 11512810.; // #

n = 0;
if (gray > 0.5) n = 24971331;

filter-i
現在是只輸出 i, 了!

單色的 i

原本輸出的值是包含 RGB 三種顏色,
輸出單色時,最簡單的做法就是...
三個色版輸出相同顏色 ( col.r 、 col.g 或 col.b )

// fragColor = vec4(col, 1.0);
fragColor = vec4(vec3(col.r), 1.0);

將原本輸出的 fragColor 註解掉,改成輸出成 vec4(vec3(col.r), 1.0)
vec3(col.r) 等同於輸出 vec3(col.r, col.r, col.r)
RGB的值皆相同時輸出單色,例如 #000 ~ #FFF

調整結果:
gray-i

反白!

此時輸出的畫面是黑底反白,如果希望是白底反黑時:
將剛剛的顏色翻過來就好!

fragColor = vec4(vec3(1.-col.r), 1.0);

由於 Shader 裡顏色的範圍是 0. ~ 1.
因此要直接反白的時候,用 1. 減掉顏色 即可
也就是剛剛 vec3(1.-col.r)

調整結果:
reverse-i


PixiJS 的 Ascii Filter:

這段 Shadertoy 程式碼裡有一段註解

// Bitmap to ASCII (not really) fragment shader by movAX13h, September 2013
// This is the original shader that is now used in PixiJs, FL Studio and various other products.

PixiJS 的 Ascii Filter 是這段 Fragment Shader 的實作,也可以依照本文調整喔
PixiJS ascii filter
PixiJS Filters Demo


上一篇
[WebGL - Day22] PixiJS 裡實作圓形淡入效果濾鏡
下一篇
[WebGL - Day24] Metaball (1/2) - 原理與在 Photoshop 實作
系列文
寫給工程師的 WebGL 學習心得30

尚未有邦友留言

立即登入留言