GLSL/C++ 实现滤镜效果
入門效果之浮雕
"浮雕"圖象效果是指圖像的前景前向凸出背景。常見于一些紀(jì)念碑的雕刻上。要實(shí)現(xiàn)浮雕事實(shí)上很easy。我們把圖象的一個(gè)象素和左上方的象素進(jìn)行求差運(yùn)算。并加上一個(gè)灰度。這個(gè)灰度就是表示背景顏色。這里我們?cè)O(shè)置這個(gè)插值為128 (圖象RGB的值是0-255)。同一時(shí)候,我們還應(yīng)該把這兩個(gè)顏色的差值轉(zhuǎn)換為亮度信息.否則浮雕圖像會(huì)出現(xiàn)彩色。
C++版
void relief(int x,int y,BYTE *pre,BYTE *now,int Stride,int index){int upperleft;upperleft=(y-1)*Stride+4*(x-1);Vec4 Pixel,NowPixel;NowPixel.SetPixel(pre,index);Pixel.SetPixel(pre,upperleft);Pixel=NowPixel-Pixel;BeGray(Pixel);Pixel.GetPixelToNow(now,index); }???
入門效果之馬賽克
接下來我們完畢一個(gè)更加常見的效果—馬賽克.圖片的馬賽克就是把圖片的一個(gè)相當(dāng)大小的區(qū)域用同一個(gè)點(diǎn)的顏色來表示.能夠覺得是大規(guī)模的減少圖像的分辨率,而讓圖像的一些細(xì)節(jié)隱藏起來, 比方電視中要秀一下某個(gè)罪犯的身材,卻又不能展示他的臉,這個(gè)時(shí)候我們就能夠給他的臉加一個(gè)馬賽克.
用HLSL代碼實(shí)現(xiàn)馬賽克是很easy的,可是相同的,我們須要一些額外的步驟,第一步就是先把紋理坐標(biāo)轉(zhuǎn)換成圖像實(shí)際大小的整數(shù)坐標(biāo).接下來,我們要把圖像這個(gè)坐標(biāo)量化---比方馬賽克塊的大小是8x8象素。那么我們能夠用下列方法來得到馬賽克后的圖像採樣值,如果[x.y]為圖像的整數(shù)坐標(biāo):
[x,y]mosaic = [ int(x/8)*8 , int(y/8)*8].
"precision mediump float; \n""varying vec2 v_texCoord; \n""uniform sampler2D s_baseMap;\n""uniform vec2 TexSize; \n""vec2 mosaicSize = vec2(8,8);\n""void main() \n""{ \n"" vec2 intXY = vec2(v_texCoord.x*TexSize.x, v_texCoord.y*TexSize.y); \n"" vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x,floor(intXY.y/mosaicSize.y)*mosaicSize.y); \n"" vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x,XYMosaic.y/TexSize.y); \n"" vec4 baseMap = texture2D(s_baseMap,UVMosaic); \n"" gl_FragColor = baseMap; \n""} \n";C++?版
void Mosaic(int x,int y,BYTE *pre,BYTE *now,int Stride,int index){Vec4 Pixel,Pixel_UpperLeft;Pixel.SetPixel(pre,index);if (x%8==0&&y%8==0){Pixel.GetPixelToNow(now,index);} else{int tmpX,tmpY;tmpX=x/8*8;tmpY=y/8*8;int index_UpperLeft;index_UpperLeft=tmpY*Stride+4*(tmpX);Pixel_UpperLeft.SetPixel(pre,index_UpperLeft);Pixel_UpperLeft.GetPixelToNow(now,index);} }讀者可能會(huì)發(fā)現(xiàn)這個(gè)馬賽克太普通了,確實(shí)它不夠新穎,以下我們來改良一下。我們希望達(dá)到這樣一個(gè)效果:馬賽克區(qū)域不是方的,而是圓的,圓形區(qū)域以外,我們用圖像原來的顏色覆蓋。這樣我們須要改變一下代碼。
首先求出原來馬賽克區(qū)域的正中心(原來是左上角):然后計(jì)算圖像採樣點(diǎn)到這個(gè)中心的距離,假設(shè)在馬賽克圓內(nèi)。就用區(qū)域的中心顏色,否則就用原來的顏色。
改良后的代碼例如以下。這里我們把馬賽克區(qū)域大小調(diào)節(jié)成16x16。這樣效果更明顯。
"precision highp float; \n""varying vec2 v_texCoord; \n""uniform sampler2D s_baseMap; \n""uniform vec2 TexSize; \n""vec2 mosaicSize = vec2(8,8); \n""void main() \n""{ \n"" vec2 intXY = vec2(v_texCoord.x*TexSize.x, v_texCoord.y*TexSize.y); \n"" vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x,floor(intXY.y/mosaicSize.y)*mosaicSize.y) + 0.5*mosaicSize; \n"" vec2 delXY = XYMosaic - intXY; \n"" float delL = length(delXY); \n"" vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x,XYMosaic.y/TexSize.y); \n"" vec4 _finalColor; \n"" if(delL< 0.5*mosaicSize.x) \n"" _finalColor = texture2D(s_baseMap,UVMosaic); \n"" else \n"" _finalColor = texture2D(s_baseMap,v_texCoord); \n"" gl_FragColor = _finalColor; \n""} \n" ;C++版
void Mosaic_Point(int x,int y,BYTE *pre,BYTE *now,int Stride,int index){Vec4 Pixel,Pixel_UpperLeft;Pixel.SetPixel(pre,index);int i,j;i=x%8;j=y%8;double dist=sqrt(double((4-i)*(4-i)+(4-j)*(4-j)));if (dist>4){Pixel.GetPixelToNow(now,index);} else{int tmpX,tmpY;tmpX=x/8*8;tmpY=y/8*8;int index_UpperLeft;index_UpperLeft=tmpY*Stride+4*(tmpX);Pixel_UpperLeft.SetPixel(pre,index_UpperLeft);Pixel_UpperLeft.GetPixelToNow(now,index);} }圖:? 改良后的馬賽克效果
l? 進(jìn)階效果之銳化模糊
以上兩個(gè)效果相對(duì)照較簡(jiǎn)單,姑且稱之為入門效果。 它并沒實(shí)用到太多數(shù)字圖像處理或者信號(hào)處理方面的知識(shí)。
接下來我們要介紹略微復(fù)雜一點(diǎn)的效果。第一個(gè)就是圖像的模糊和銳化。
圖像的模糊又成為圖像的平滑(smoothing),我們知道人眼對(duì)高頻成分是非常敏感的。假設(shè)在一個(gè)亮度連續(xù)變化的圖像中,突然出現(xiàn)一個(gè)亮點(diǎn),那么我們非常easy察覺出來,類似的,假設(shè)圖像有個(gè)突然的跳躍—明顯的邊緣,我們也是非常easy察覺出來的。
這些突然變化的分量就是圖像的高頻成分。
人眼一般是通過低頻成分來辨別輪廓,通過高頻成分來感知細(xì)節(jié)的(這也是為什么照片分辨率低的時(shí)候,人們僅僅能辨認(rèn)出照片的大概輪廓,而看不到細(xì)節(jié))。可是這些高頻成分通常也包括了噪聲成分。圖像的平滑處理就是濾除圖像的高頻成分。
那么怎樣才干濾除圖像的高頻成分呢?我們先來介紹一下圖像數(shù)字濾波器的概念。
簡(jiǎn)單通俗的來說,圖像的數(shù)字濾波器事實(shí)上就是一個(gè)n x n的數(shù)組(數(shù)組中的元素成為濾波器的系數(shù)或者濾波器的權(quán)重。n稱為濾波器的階)。
對(duì)圖像做濾波的時(shí)候。把某個(gè)像素為中心的nxn個(gè)像素的值和這個(gè)濾波器做卷積運(yùn)算(也就是相應(yīng)位置上的像素和相應(yīng)位置上的權(quán)重的乘積累加起來),公式例如以下
?當(dāng)中x , y 為當(dāng)前正在處理的像素坐標(biāo)。
通常情況下,我們?yōu)V波器的階數(shù)為3已經(jīng)足夠了,用于模糊處理的3x3濾波器例如以下
?????? ?????????????????????????。
經(jīng)過這種濾波器。事實(shí)上就是等效于把一個(gè)像素和周圍8個(gè)像素一起求平均值,這是很合理的---等于把一個(gè)像素和周圍幾個(gè)像素?cái)嚢柙谝黄稹匀痪湍:恕?C++版
以上的模糊濾波器稱為BOX濾波器,是最簡(jiǎn)單的濾波器。假設(shè)考慮到離開中心像素的距離對(duì)濾波器系數(shù)的影響,我們通常採用更加合理的濾波器---高斯濾波器—一種通過2維高斯採樣得到的濾波器。它的模板例如以下:
非常easy看出來。離開中心越遠(yuǎn)的像素。權(quán)重系數(shù)越小。
對(duì)于銳化操作,經(jīng)常使用的銳化模板是拉普拉斯(Laplacian)模板,這個(gè)模板定義例如以下:
easy看出拉普拉斯模板的作法:先將自身與周圍的8個(gè)象素相減,表示自身與周圍象素的區(qū)別。再將這個(gè)區(qū)別加上自身作為新象素的灰度。
可見,假設(shè)一片暗區(qū)出現(xiàn)了一個(gè)亮點(diǎn),那么銳化處理的結(jié)果是這個(gè)亮點(diǎn)變得更亮,這就增強(qiáng)了圖像的細(xì)節(jié)。
以下三副圖分別表示了經(jīng)過BOX濾波。高斯濾波和拉普拉斯濾波后的圖像
??BOX 模糊????????????????? 高斯模糊??????????????????? 拉普拉斯銳化
高斯模糊和拉普拉斯銳化效果的GLSL和BOX的代碼基本一致,就是filter的系數(shù)不同。這里不在列出。
通過這個(gè)兩個(gè)效果。我們介紹了圖像的濾波操作,這種操作,也成為模板操作。它實(shí)現(xiàn)了一種鄰域運(yùn)算(Neighborhood Operation),即某個(gè)象素點(diǎn)的結(jié)果灰度不僅和該象素灰度有關(guān)。并且和其鄰域點(diǎn)的值有關(guān)。模板運(yùn)算在圖象處理中常常要用到。能夠看出。它是一項(xiàng)很耗時(shí)的運(yùn)算。有一種優(yōu)化的方法稱為可分離式濾波,就是使用兩個(gè)pass來進(jìn)行x/y方向分別濾波,能讓運(yùn)算次數(shù)大大降低。
并且濾波器階數(shù)越高,優(yōu)勢(shì)越明顯。
數(shù)字圖像濾波的時(shí)候,相同還須要注意邊界像素的問題,只是幸好,GLSL能讓邊界處理更加的透明和簡(jiǎn)單。
l? 進(jìn)階效果之描邊效果
相對(duì)浮雕效果來說。描邊(邊緣檢測(cè))的代碼并不復(fù)雜多少,僅僅是在理論上相對(duì)來說略微復(fù)雜一點(diǎn),并且效果看上去更加的討人喜歡一些。
我們知道 ,假設(shè)在圖像的邊緣處。灰度值肯定經(jīng)過一個(gè)跳躍。我們能夠計(jì)算出這個(gè)跳躍,并對(duì)這個(gè)值進(jìn)行一些處理,來得到邊緣濃黑的描邊效果。
首先我們能夠考慮對(duì)這個(gè)象素的左右兩個(gè)象素進(jìn)行差值。得到一個(gè)差量。這個(gè)差量越大,表示圖像越處于邊緣,并且這個(gè)邊緣應(yīng)該左右方向的,相同我們能得到上下方向和兩個(gè)對(duì)角線上的圖像邊緣。這樣我們構(gòu)造一個(gè)濾波器
經(jīng)過這個(gè)濾波器后。我們得到的是圖像在這個(gè)象素處的變化差值,我們把它轉(zhuǎn)化成灰度值,并求絕對(duì)值(差值可能為負(fù)),然后我們定義差值的絕對(duì)值越大的地方越黑(邊緣顯然是黑的)。否則越白,我們便得到例如以下的效果:
圖:鉛筆描邊效果
該效果的代碼例如以下(當(dāng)中dip_filter函數(shù)代碼同上):
"precision mediump float; \n""vec4 dip_filter(mat3 _filter, sampler2D _image, vec2 _xy, vec2 texSize) \n""{ \n"" mat3 _filter_pos_delta_x=mat3(vec3(-1.0, 0.0, 1.0), vec3(0.0, 0.0 ,1.0) ,vec3(1.0,0.0,1.0)); \n"" mat3 _filter_pos_delta_y=mat3(vec3(-1.0,-1.0,-1.0),vec3(-1.0,0.0,0.0),vec3(-1.0,1.0,1.0)); \n"" vec4 final_color = vec4(0.0, 0.0, 0.0, 0.0); \n"" for(int i = 0; i<3; i++) \n"" { \n"" for(int j = 0; j<3; j++) \n"" { \n"" vec2 _xy_new = vec2(_xy.x + _filter_pos_delta_x[i][j], _xy.y + _filter_pos_delta_y[i][j]); \n"" vec2 _uv_new = vec2(_xy_new.x/texSize.x, _xy_new.y/texSize.y); \n"" final_color += texture2D(_image,_uv_new) * _filter[i][j]; \n"" } \n"" } \n"" return final_color; \n""} \n""varying vec2 v_texCoord; \n""uniform vec2 TexSize; \n""uniform sampler2D s_baseMap; \n""void main() \n""{ \n"" vec2 intXY = vec2(v_texCoord.x * TexSize.x, v_texCoord.y * TexSize.y); \n"" mat3 _smooth_fil = mat3(-0.5,-1.0,0.0, \n"" -1.0,0.0,1.0, \n"" 0.0,1.0,0.5); \n"" vec4 delColor = dip_filter(_smooth_fil, s_baseMap, intXY, TexSize); \n"" float deltaGray = 0.3*delColor.x + 0.59*delColor.y + 0.11*delColor.z; \n"" if(deltaGray < 0.0) deltaGray = -1.0 * deltaGray; \n"" deltaGray = 1.0 - deltaGray; \n"" gl_FragColor = vec4(deltaGray,deltaGray,deltaGray,1.0); \n""} \n";C++版
void Gaussian_filter(int x,int y,BYTE *pre,BYTE *now,int Stride,int index) {int dir_x[3][3]={-1,0,1,-1,0,1,-1,0,1};int dir_y[3][3]={1,1,1,0,0,0,-1,-1,-1};int tmpX,tmpY;Vec4 Pixel,PrePixel;double filter[3][3]={1.0/16,2.0/16,1.0/16,2.0/16,4.0/16,2.0/16,1.0/16,2.0/16,1.0/16};Pixel.Clear();for (int i=0;i<3;i++){for (int j=0;j<3;j++){tmpX=x+dir_x[i][j];tmpY=y+dir_y[i][j];int tmp=tmpY*Stride+4*(tmpX);PrePixel.SetPixel(pre,tmp);Pixel+=PrePixel*filter[i][j];}}//BeGray(Pixel);Pixel.GetPixelToNow(now,index); }以上是簡(jiǎn)單的邊緣檢測(cè)算子。更加嚴(yán)格的。我們能夠採樣Sobel算子,Sobel 算子有兩個(gè),一個(gè)是檢測(cè)水平邊沿的,還有一個(gè)是檢測(cè)垂直平邊沿的。相同,Sobel算子還有一種形式是各向同性Sobel算子。也有兩個(gè),一個(gè)是檢測(cè)水平邊沿的,還有一個(gè)是檢測(cè)垂直邊沿的。各向同性Sobel算子和普通Sobel算子相比,它的位置加權(quán)系數(shù)更為準(zhǔn)確,在檢測(cè)不同方向的邊沿時(shí)梯度的幅度一致。讀者能夠自行嘗試Sobel算子的效果,僅僅要改動(dòng)pencil_filter的值就能夠了。
l? 高級(jí)效果之偽 HDR/Blow
HDR和Blow在如今主流游戲中是很時(shí)髦的效果。
所謂HDR就是高動(dòng)態(tài)范圍的意思,我們知道。在普通的顯示器和位圖里。每通道都是8-bit,也就是說RGB分量的范圍都是0-255,這用來表示現(xiàn)實(shí)中的顏色顯然是遠(yuǎn)遠(yuǎn)不夠的,現(xiàn)實(shí)中的圖像的動(dòng)態(tài)范圍遠(yuǎn)遠(yuǎn)大的多,那么怎樣在現(xiàn)有的顯示設(shè)備里盡可能的保持更大的動(dòng)態(tài)范圍,并且讓它能更符合人眼的習(xí)慣就成了圖形學(xué)研究的一個(gè)熱點(diǎn)。通常真正的HDR的做法都是採用浮點(diǎn)紋理。把渲染運(yùn)算的過程中,我們使用16bit的動(dòng)態(tài)范圍來保存運(yùn)算結(jié)果,然后我們對(duì)運(yùn)算結(jié)果進(jìn)行分析。求出這個(gè)圖像的中間灰度值,然后對(duì)圖像進(jìn)行調(diào)整映射到LDR的設(shè)備中。可是這種算法有兩個(gè)很耗資源的過程,當(dāng)中一個(gè)是浮點(diǎn)紋理,另外一個(gè)就是求圖像中間灰度(通常情況是把圖像不停的渲染到RenderTarget。每渲染一次,圖像大小縮小一半。直到縮小到1x1大,一個(gè)1024 x1024的圖像須要渲染10次!)。因此盡管HDR的效果很美麗。可是眼下還是僅僅有為數(shù)不多的游戲採用了這種算法,大部分都是採用的偽HDR+blow效果。
偽HDR效果一般是又一次調(diào)整圖像的亮度曲線。讓亮的更亮,暗的更暗一些,而Blow效果則是圖像的亮度擴(kuò)散開來。產(chǎn)生非常柔的效果。
在這里我們採用一個(gè)二次曲線來又一次調(diào)整圖像的亮度,這個(gè)曲線的方程是
x [ (2-4k) x + 4k-1 ).K的取值范圍為0.5 – 2.0
經(jīng)過這個(gè)公式調(diào)整以后,圖像上亮的區(qū)域?qū)⒏拥牧痢6铱傮w亮度會(huì)提高。那么接下來,我們?cè)鯓邮箞D像的亮度擴(kuò)散開來呢?一種可行的方法就是對(duì)場(chǎng)景圖像做一次downsample。
把它變成原來的1/4次大小,那樣就等于亮度往外擴(kuò)散了4x4個(gè)象素的區(qū)域。
"precision mediump float; \n""varying vec2 v_texCoord; \n""uniform sampler2D s_baseMap; \n""uniform float k; \n""vec4 xposure(vec4 _color, float gray, float ex) \n""{ \n"" float b = (4.0*ex - 1.0); \n"" float a = 1.0 - b; \n"" float f = gray*(a*gray + b); \n"" return f*_color; \n""} \n""void main() \n""{ \n"" vec4 _dsColor = texture2D(s_baseMap, v_texCoord); \n"" float _lum = 0.3*_dsColor.x + 0.59*_dsColor.y; \n"" vec4 _fColor = texture2D(s_baseMap, v_texCoord); \n"" gl_FragColor = xposure(_fColor, _lum, k); \n""} \n";C++版
void HDR(int x,int y,BYTE *pre,BYTE *now,int index,double k){Vec4 Pixel;double GrayPixel;Pixel.SetPixel(pre,index);GrayPixel=GetGray(Pixel)/255.0;double b=(4*k-1.0);double a=1-b;double f=GrayPixel*(a*GrayPixel+b);Pixel*=f;OverFlow(Pixel);Pixel.GetPixelToNow(now,index); }以下是原圖像和經(jīng)過處理后圖像的對(duì)照:
??
原圖????????????????????? k = 1.1????????????????????? k = 1.6
圖:經(jīng)過偽HDR+Blow處理過的圖像和原圖的對(duì)照
l? 高級(jí)效果之水彩化
真正的水彩效果在shader中是比較難實(shí)現(xiàn)的。它須要進(jìn)行中值濾波后累加等一些操作,還須要處理NPR中的筆觸一類的概念。本文繞開這些概念,僅僅從視覺效果上能盡量模擬出水彩的畫的那種感覺來。
我們知道,水彩畫一個(gè)最大的特點(diǎn)是水彩在紙上流動(dòng)擴(kuò)散后會(huì)和周圍的顏色攪拌在一起,另外一個(gè)特點(diǎn)就是水彩一般會(huì)形成一個(gè)個(gè)的色塊,過渡不像照片那樣的平滑。
針對(duì)這兩個(gè)特點(diǎn)。
我們能夠設(shè)計(jì)這種一個(gè)算法來模擬水彩畫的效果。
我們能夠採用噪聲紋理的方式。既事先計(jì)算好一個(gè)nxn的隨機(jī)數(shù)數(shù)組,作為紋理傳遞給Pixel shader,這樣在Pixel Shader里我們就能獲得隨機(jī)數(shù)了。得到隨機(jī)數(shù)后,我們將隨機(jī)數(shù)映射成紋理坐標(biāo)的偏移值,就能模擬出色彩的擴(kuò)散了。典型的噪聲紋理是這個(gè)樣子的:圖:噪聲紋理
接下來我們須要處理色塊,我們對(duì)顏色的RGB值分別進(jìn)行量化,把RGB分量由原來的8bit量化成比特?cái)?shù)更低的值。這樣顏色的過渡就會(huì)顯得不那么的平滑,而是會(huì)呈現(xiàn)出一定的色塊效果。
通過以上兩步處理后,我們得到的圖像依舊有非常多的細(xì)節(jié)。尤其是第一步處理中產(chǎn)生的非常多細(xì)節(jié)噪點(diǎn),非常自然的我們就想到通過平滑模糊的方式來過濾掉這些高頻噪聲成分。
算法設(shè)計(jì)好了,接下來看看我們?cè)鯓釉赗enderMonkey里實(shí)現(xiàn)這個(gè)算法。
類似上一個(gè)效果。我們須要兩個(gè)pass來完畢這個(gè)算法,第一個(gè)pass叫flow pass,模擬顏色的流動(dòng)和處理顏色的量化。
第二個(gè)pass叫Gauss pass。也就是前面提到的高斯模糊算法。
我們的重點(diǎn)在第一個(gè)pass。
在模擬擴(kuò)散的pass中,我們相同須要一個(gè)RenderTarget。以把結(jié)果保存在當(dāng)中以便興許處理,然后還須要一個(gè)噪聲紋理來產(chǎn)生隨機(jī)數(shù)。詳細(xì)代碼例如以下:
"precision mediump float; \n""varying vec2 v_texCoord; \n""uniform sampler2D s_baseMap; \n""uniform vec2 TexSize; \n""float _waterPower = 40.0; \n""float _quatLevel = 5.0; \n""vec4 quant(vec4 _cl, float n) \n""{ \n"" _cl.x = floor(_cl.x*255.0/n)*n/255.0; \n"" _cl.y = floor(_cl.y*255.0/n)*n/255.0; \n"" _cl.z = floor(_cl.z*255.0/n)*n/255.0; \n"" return _cl; \n""} \n""void main() \n""{ \n"" vec4 noiseColor = _waterPower*texture2D(s_baseMap,v_texCoord); \n"" vec2 newUV =vec2 (v_texCoord.x + noiseColor.x/TexSize.x,v_texCoord.y + noiseColor.y/TexSize.y); \n"" vec4 _fColor = texture2D(s_baseMap,newUV); \n"" gl_FragColor = quant(_fColor, 255.0/pow(2,_quatLevel)); \n""} \n";C++版
void WaterFilter(int x,int y,BYTE *pre,BYTE *now,int Stride,int index,double _quatLevel,double _waterPower,int width,int height){Vec4 nowPixel,RoundPixel;int indexRound;double Level;nowPixel.SetPixel(pre,index);int rx=rand()%2,ry=rand()%2;indexRound=(y+rx)*Stride+(x+ry)*4;RoundPixel.SetPixel(pre,indexRound);Level=255/pow(2,_quatLevel);RoundPixel.r=floor(RoundPixel.r/Level)*Level;RoundPixel.g=floor(RoundPixel.g/Level)*Level;RoundPixel.b=floor(RoundPixel.b/Level)*Level;OverFlow(RoundPixel);RoundPixel.GetPixelToNow(now,index); }代碼中的_quatLevel用來表示對(duì)圖像的量化比特?cái)?shù)。值越小。色塊越明顯。比較合理的取值范圍是2-6。_waterPower則表示圖像顏色擴(kuò)散范圍,取值范圍在8-64之間的效果比較好。
以下是經(jīng)過水彩畫處理后的圖像:
?
?圖:水彩畫效果。左圖量化比特?cái)?shù)為6比特,擴(kuò)散范圍為20象素。
???????????????? 右圖量化比特?cái)?shù)為5比特,擴(kuò)散范圍為40象素
最后貼個(gè)C++版用到的函數(shù)
struct Vec4{double r,g,b,a;Vec4 (){this->r=0;this->g=0;this->b=0;this->a=0;}Vec4(double r,double g,double b){this->r=r;this->g=g;this->b=b;}Vec4 operator+(const double one) const{return Vec4(r+one,g+one,b+one);}Vec4 operator+(const Vec4& rhs)const{return Vec4(r+rhs.r,g+rhs.g,b+rhs.b);}Vec4& operator+=(const double one){r+=one;g+=one;b+=one;return *this;}Vec4& operator+=(const Vec4& rhs){r+=rhs.r;g+=rhs.g;b+=rhs.b;return *this;}Vec4 operator-(const double one) const{return Vec4(r-one,g-one,b-one);}Vec4 operator-(const Vec4& rhs)const{return Vec4(r-rhs.r,g-rhs.g,b-rhs.b);}Vec4& operator-=(const double one){r-=one;g-=one;b-=one;return *this;}Vec4 operator*(const double one) const{return Vec4(r*one,g*one,b*one);}Vec4& operator*=(const double one){r*=one;g*=one;b*=one;return *this;}Vec4 operator/(const double one) const{return Vec4(r/one,g/one,b/one);}Vec4& operator/=(const double one){r/=one;g/=one;b/=one;return *this;}void Clear(){r=g=b=0;}void SetPixel(BYTE *pre,int index){this->r=pre[index];this->g=pre[index+1];this->b=pre[index+2];this->a=pre[index+3];}void SetPixel(double RGB){this->r=RGB;this->g=RGB;this->b=RGB;}void SetPixel(double R,double G,double B){this->r=R;this->g=G;this->b=B;}void GetPixelToNow(BYTE *now,int index){now[index]=this->r;now[index+1]=this->g;now[index+2]=this->b;} }; void OverFlow(Vec4 &Pixel){if (Pixel.r>255.0){Pixel.r=255;}else if (Pixel.r<0.0){Pixel.r=-Pixel.r;}if (Pixel.g>255.0){Pixel.g=255;}else if (Pixel.g<0.0){Pixel.g=-Pixel.g;}if (Pixel.b>255.0){Pixel.b=255;}else if (Pixel.b<0.0){Pixel.b=-Pixel.b;} } void OverFlow(double &Pixel){if (Pixel>255.0){Pixel=255;}else if (Pixel<0.0){Pixel=-Pixel;} } void ToGray(Vec4 &Pixel){double detaGray;detaGray=Pixel.r*0.3+Pixel.g*0.59+Pixel.b*0.11;Pixel.SetPixel(detaGray);OverFlow(Pixel); } double GetGray(Vec4 Pixel){double detaGray;detaGray=Pixel.r*0.3+Pixel.g*0.59+Pixel.b*0.11;OverFlow(detaGray);return detaGray; } void BeGray(Vec4 &Pixel){double detaGray;detaGray=Pixel.r*0.3+Pixel.g*0.59+Pixel.b*0.11;if (detaGray<0.0){detaGray=-detaGray;}else if (detaGray>255.0){detaGray=255;}detaGray=255-detaGray;Pixel.SetPixel(detaGray); } void Solve(){BYTE *pre = (BYTE*)m_srcImg.Scan0;BYTE *now = (BYTE*)m_copySrcImg.Scan0;//函數(shù)在這邊調(diào)用就可以。?? ?int index=0,Stride=m_copySrcImg.Stride; ?? ?int width=m_pCopyImg->GetWidth(); ?? ?int height=m_pCopyImg->GetHeight(); ?? ?index=Stride+4; ?? ?for (int j=1;j<m_copySrcImg.Height-1;j++) ?? ?{ ?? ??? ?for (int i=1;i<m_copySrcImg.Width-1;i++) ?? ??? ?{ ?? ??? ??? //Gaussian_filter(i,j,pre,now,Stride,index); ?? ??? ??? ?//dip_filter(i,j,pre,now,Stride,index); ?? ??? ??? ?//relief(i,j,pre,now,Stride,index); ?? ??? ??? ?//Mosaic(i,j,pre,now,Stride,index); ?? ??? ??? ?//Mosaic_Point(i,j,pre,now,Stride,index); ?? ??? ??? ?//HDR(i,j,pre,now,index,1.1); ?? ??? ??? ?//WaterFilter(i,j,pre,now,Stride,index,10,40,width,height);?? ? ?? ??? ??? ?index+=4; ?? ??? ?} ?? ??? ?index+=8; ?? ?} ?? ?//調(diào)用函數(shù)。
}
轉(zhuǎn)載于:https://www.cnblogs.com/lxjshuju/p/7343873.html
總結(jié)
以上是生活随笔為你收集整理的GLSL/C++ 实现滤镜效果的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: fixed 定位 苹果手机输入框触发时内
- 下一篇: s3c2440移植MQTT