matlab中blur函数_游戏中的PostProcessing(后处理)
PostProcessing是現(xiàn)代游戲中必不可少的技術(shù)之一,本文簡單來總結(jié)下PostProcessing的實現(xiàn)原理和應(yīng)用.因為詳細寫起來需要很大篇幅且很費時間,這里只簡單介紹下原理.
1.基礎(chǔ)部分
PostProcessing,通常在普通的場景渲染結(jié)束后對結(jié)果進行處理,將一張或數(shù)張Texture處理得到一張新的Texture.
PostProcessing的渲染Pipeline普通的模型渲染一樣,不同之處在于在VertexShader中通常只是簡單的拷貝,主要的邏輯寫在Pixel Shader中.
拷貝圖片的圖元是什么樣的呢?直覺來看就是一個長方形圖元,由兩個三角形組成.但是一般是通過一個大一點單個三角形圖元來實現(xiàn)的,超出長方形框外的部分在光柵化時會被自動過濾掉,這樣寫起來更簡潔,運行速度也稍快一些.
一個三角形的渲染拷貝作用和一個長方形是一樣的需要注意的是,OpenGL中Texture左下角UV點是(0,0),而DirectX中左上角UV是(0,0).
另外Direct3D 9中有Half-Pixel Offset的問題,采樣像素時需要將uv點偏移半像素或者計算頂點坐標時時偏移半像素(游戲引擎里一般是直接處理編譯好的shader,這樣不用為不同版本寫不同的shader),來看下微軟文檔里的幾張圖,就能直觀地知道Half-Pixel 是什么問題:
Direct3D 10 Pixel CoordinateDirect3D 9 Pixel CoordinateDirect3D Texel Coordinate2.UV的使用
根據(jù)Texture中點UV的值,施加不同的效果,比如常見的Vignette效果,就是根據(jù)到屏幕中心點的距離,混合黑色的顏色,產(chǎn)生邊緣灰暗的效果,非常的簡單.
Vignette效果根據(jù)Texture中像素點的uv值做偏移,可以得到一些變形,扭曲,波浪,鏡頭變形等效果.比如下面的一個波浪的效果,FS代碼大致是這樣的:
波浪效果vec2 v = UV - vec2(0.5, 0.5); vec2 offset = sin((length(v) * 5 + TIME) * PI * 2) * normalize(v) * 0.1; COLOR = texture(TEXTURE, UV + offset);根據(jù)到中心點的距離,用正弦函數(shù)計算沿著中心方向UV的偏移量,就可以得到波浪的效果.然后在相位上加上時間,可以得到隨時間變化的動態(tài)波浪.
3.Noise Texture/Lookup Texture
噪聲圖也是非常常用的一種非常常用的Texture,通常我們見到的都是Perlin Noise Texture,長得是這樣的:
Perlin Noise Texture比如用Perlin Noise Texture來生成一個Dissolve的效果,就是指定一個閾值,動態(tài)clip掉Perlin Noise Texture中相應(yīng)點的灰度值在閾值下的點:
Dissolve隨時間改變這個閾值,就可以得到逐漸出現(xiàn)或者消失的Dissolve效果.
還有的Texture保存一些(u,v)到某些值的映射,叫做Loopup Texture(LUT).很多張Texture組成Array還可以完成(x,y,z)到值的映射,叫做3D LUT.
LUT最常見的應(yīng)用就是用來做Color Grading.
一個用來ColorGrading的LUT4.Image Kernels
將在UV當前點周圍數(shù)點采樣,然后將多個點處理結(jié)果相加,這樣的處理寫成矩陣叫做Image Kernel,這樣的操作也可以叫做Convolution,比如一個邊緣檢測的Kernel是這樣的:
將中心點的像素值X8,然后減去周圍點的像素值,就可以做邊緣檢測.
Edge Detection一些常見的如Sharpen, Blur等各種各樣的效果都是通過這種方式實現(xiàn)的.
5.Gaussian Blur
從信號和采樣的角度來說,有這樣幾種Filtering:
box filter,tent filter,sinc filter 所有filter的面積和是1Sinc filtering計算起來最復(fù)雜,效果也是最好的.
Filtering來重構(gòu)信號的過程大致是這樣:
sinc filterGuassian Blur可以看作是Sinc Filter的一種近似,用這樣一個距離相關(guān)的Kernel 函數(shù):
實際操作時,忽略較遠處的值,用預(yù)計算好的權(quán)重值,比如一個5X5的Kernel:
5X5 Guassion weight這樣每次計算時需要采樣25個點才能得到最終的像素值,因此可以將Guassion blur分成兩次Pass來實現(xiàn),Kernel是這樣的:
兩次Pass的Guassion weight這樣每個點需要的采樣次數(shù)就降到了10次.Box-filter(周圍N*N個點權(quán)重相等)也是可以像這樣分成兩次Pass的,這樣的Kernel是separable的.
當GPU從貼圖中采樣時,使用bilinear方式在某個點采樣時,是根據(jù)點的位置從目標點周圍4點中取值加權(quán)平均,利用這點,也可以減少需要的采樣次數(shù).比如一個3X3的Box-filter時,所有點的權(quán)重相等,將采樣目標點放到4點中心,就可以得到周圍4點的平均值:
9個采樣點降到5個和4個Guassion blur也可以通過這中方法來減少采樣數(shù),兩種方式結(jié)合起來,可以使采樣數(shù)達到最小.
6.DownSample/UpSample
一些需要Blur效果的PostProcessing通常將將TextureDownSample到一個較小的尺寸,自帶Blur的效果基本上不會影響最總的效果,較小的Texure還可以加快運行速度.
Bloom的效果,就是使較亮的部分更亮點,并且擴散到附近.就是通過一系列的DownSample和UpSample,最后疊加到原來圖片上來實現(xiàn)的.
Bloom 4次DownSampleBloom 4次UpSample和最終顏色疊加的效果7.Depth Buffer
正常地渲染場景后,可以得到Depth Buffer.PostProcessing時,從當前的UV值,和Depth Buffer中讀取到的深度值,可以得到裁剪空間中的坐標,再從裁剪空間可以反推出當前像素點在世界空間中的位置.
需要注意的是,OpenGL中是將-1~1的深度值映射到0~1的Depth Buffer的深度值,從中讀取時需要將得到的值應(yīng)用d * 2 - 1,得到在裁剪空間中的深度值.
得到世界空間中的坐標后,就可以應(yīng)用一些效果,比如Depth Fog效果,就是計算點到攝影機的距離,根據(jù)距離用對數(shù)函數(shù)計算出一個霧的強度值,將霧的顏色疊加到原始顏色上,就可以得到Depth Fog效果.也可以指定一個地面的高度,根據(jù)到地面的距離得到Height Fog的效果.
再看下Depth of Field(DOF 景深)的效果,DOF先設(shè)置一個對焦的距離和范圍,超出對焦范圍的部分做blur處理.一個簡單實現(xiàn)的DOF的大致過程如下:
a.將原圖DownSample到1/4大小.
b.確定模糊的強度,模糊強度隨距離變化的曲線大致如下圖所示,根據(jù)深度值計算每個點的模糊強度,保存到一張Texture中.
dof強度和距離的關(guān)系c.對DownSample的Texture進行Blur,每個點使用上面的Texture中的模糊強度計算模糊半徑.用模糊半徑在一個預(yù)計算好的Disk中隨機分布的點位置進行采樣,點的分布大致是下面的圖片中所示.
d.將得到的Texture用9-tap Tent Filter再次Blur.
e.將得到的Texture和原圖按照計算出的模糊強度混合,得到DOF處理后的圖片.
DOF8.Motion Blur/Temporal AA
Motion Blur的實現(xiàn):每個物體記錄上一幀的位置,計算出物體相對上一幀的位置偏移,算出相對鏡頭的速度值,渲染到一個Velocity Buffer上,再根據(jù)Velocity Buffer做Blur處理.
TAA:將每個物體的投影矩陣加上一點不同的Jittering,每幀的jittering值不斷變化,將連續(xù)兩幀或數(shù)幀的結(jié)果進行插值,以此來實現(xiàn)快速的AA.
實際的游戲中有的會將Motion Blur和TAA結(jié)合起來實現(xiàn)更好的效果.
這里僅僅簡單介紹下這兩種處理基本的原理,不再深入探究.
9.Compute Shader
Computer Shader非常適用于PostProcessing,因為不需要Rasterize,OutputMerge等階段,Computer Shader可以運行地比Pixel Shader更快.大部分Post Processing都可以直接切換成CS的寫法來獲得速度提升.不僅如此,CS的GroupShaderdMemory實現(xiàn)執(zhí)行單元間共享內(nèi)存,讓CS可以用來實現(xiàn)一些PS無法實現(xiàn)的功能.
來看下Auto Exposure的實現(xiàn),Auto Exposure的原理,就是將所有Texture中的像素的明亮度統(tǒng)計,計算得到一個合適的曝光度,再應(yīng)用到Texture上.
使用Pixel Shader來實現(xiàn)的話,將需要計算的Texture上每個點的明亮度,并DownSample到一個合適大小的Texture上,再將該Texture連續(xù)按照大小的1/2 DownSample 直到一個1X1的Texture上,該Texture上的值就是所有點的平均明亮度,就可以改變Texture的曝光度.
Pixel Shader的缺點就是只能得到所有點亮度的平均值或者加權(quán)平均值.使用CS則可以統(tǒng)計所有明亮度范圍并進行計數(shù),實現(xiàn)更加強大的Auto Exposure.
來看下一個簡單的AutoExposure CS的實現(xiàn):
先將Texture DownSample到某個合適的大小,再用CS計算:
[numthreads(HISTOGRAM_THREAD_X, HISTOGRAM_THREAD_Y, 1)] void KEyeHistogram(uint2 dispatchThreadId : SV_DispatchThreadID, uint2 groupThreadId : SV_GroupThreadID) {const uint localThreadId = groupThreadId.y * HISTOGRAM_THREAD_X + groupThreadId.x;// 使用其中部分Thread清零共享內(nèi)存if (localThreadId < HISTOGRAM_BINS){gs_histogram[localThreadId] = 0u;}float2 ipos = float2(dispatchThreadId) * 2.0;//Thread之間同步等待GroupMemoryBarrierWithGroupSync();if (ipos.x < _ScaleOffsetRes.z && ipos.y < _ScaleOffsetRes.w){uint weight = 1u;float2 sspos = ipos / _ScaleOffsetRes.zw;// Vignette 權(quán)重,中心位置點的權(quán)重會更高#if USE_VIGNETTE_WEIGHTING{float2 d = abs(sspos - (0.5).xx);float vfactor = saturate(1.0 - dot(d, d));vfactor *= vfactor;weight = (uint)(64.0 * vfactor);}#endiffloat3 color = _Source.SampleLevel(sampler_LinearClamp, sspos, 0.0).xyz; // Bilinear downsample 2x//計算明亮度float luminance = Luminance(color);float logLuminance = GetHistogramBinFromLuminance(luminance, _ScaleOffsetRes.xy);//根據(jù)明亮度計算得到需要累加到的bin位置uint idx = (uint)(logLuminance * (HISTOGRAM_BINS - 1u));//相應(yīng)bin位置的計數(shù)原子累加InterlockedAdd(gs_histogram[idx], weight);}//同步等待GroupMemoryBarrierWithGroupSync();//使用其中的部分Thread來將累計值從共享內(nèi)存寫入到RWBufferif (localThreadId < HISTOGRAM_BINS){InterlockedAdd(_HistogramBuffer[localThreadId], gs_histogram[localThreadId]);}這樣我們就得到帶有明亮值分布的Histogram,不僅可以用來計算平均明亮值,還可以根據(jù)分布情況作進一步優(yōu)化.
UE4中Eye Adaption的Debug10.Screen-Space Methods
有了Depth Buffer來獲取當前點再世界坐標的位置,就可以更進一步,在目標點周圍取點,判斷可見性,進行RayMarching等操作.這類方法叫做Screen-Space Method,常見的有SSAO(Screen Space Abbient Occlusion), SSR(Screen Space Reflecttion)等.
這里介紹下一個簡單的SSAO的實現(xiàn).
a.準備Depth Buffer和Normal Buffer,如果是Deferred Rendering,直接在GBUFFER中就能取到,如果是Forward Rendering,可以通過Depth Buffer生成.
b.在一個半球形的區(qū)域中隨機取點,并且加上隨機的旋轉(zhuǎn)值.將隨機到的點通過Normal Buffer計算得到世界空間中的坐標,再計算得到在裁剪空間中的值,并和DepthTexture中的深度值進行比較來判斷是否被遮擋,將結(jié)果累加,得到一個保存AO值的buffer.
SSAO Sample Kernelc.將得到的AO Buffer進行Blur.
d.根據(jù)AO值修改Diffuse的光照值,產(chǎn)生AO的效果.
SSAO的效果SSR的原理和SSAO很相似,需要用到Normal Buffer和Metal Buffer(表示鏡面光反照率,一般Gbuffer中有),根據(jù)需要計算反射光照的點的Normal和Metal值,計算得到一條反射光線,沿著這條線進行RayMarch,得到第一個一個與該射線相交的點,將該點的顏色當作反射的顏色進行混合.
步長不斷變化的RayMarchingSSR因為SSR是Screen Space的,所以無法反射屏幕之外的場景.
11.小結(jié)
(1)本文基本上介紹到了大部分比較常見的PostProcessing,其他的效果也都是通過這些方法的擴展和延伸.
(2) 各個PostProcessing的順序非常重要,比如SSR SSAO一般在渲染透明物體前執(zhí)行,DOF Bloom等一般在所有物體渲染結(jié)束后,ToneMapping之前,一些自定義的變形效果通常在ToneMapping之后.
(3)各個效果之間可能會互相干擾,比如TAA就會影響很多效果的計算,需要在計算時考慮到這些因素.
(4)各個PostProcessing之間有的可以共用一些Texture,以及在一次Pass中合并執(zhí)行多個操作,來降低性能消耗.
(5)實際項目中的應(yīng)用實現(xiàn)一般比文中提到的要復(fù)雜得多,需要細心地調(diào)節(jié)參數(shù),不斷測試改進.本文只是從原理方面簡單描述下,具體的應(yīng)用還需要參考更加詳細的文檔.
總結(jié)
以上是生活随笔為你收集整理的matlab中blur函数_游戏中的PostProcessing(后处理)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 京东极速版如何拉新(京a牌照意味着什么)
- 下一篇: 货拉拉怎么注册加入(汉典货字的基本解释)