免費(fèi)申請(qǐng)網(wǎng)站官網(wǎng)培訓(xùn)機(jī)構(gòu)退費(fèi)糾紛一般怎么解決
這次我們將使用計(jì)算機(jī)著色器對(duì)圖像進(jìn)行后處理。
要進(jìn)行后處理需要將渲染圖像從cpu傳遞給gpu,并在gpu對(duì)圖像進(jìn)行處理然后傳回cpu。
首先創(chuàng)建一個(gè)后處理基類(lèi)BasePP
首先聲明需要用到的屬性。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;[RequireComponent(typeof(Camera))]
public class BasePP : MonoBehaviour
{public ComputeShader shader = null;protected string kernelName = "CSMain";protected Vector2Int texSize = new Vector2Int(0,0);protected Vector2Int groupSize = new Vector2Int();protected Camera thisCamera;protected RenderTexture output = null;protected RenderTexture renderedSource = null;protected int kernelHandle = -1;protected bool init = false;protected virtual void Init(){if (!SystemInfo.supportsComputeShaders){Debug.LogError("It seems your target Hardware does not support Compute Shaders.");return;}if (!shader){Debug.LogError("No shader");return;}kernelHandle = shader.FindKernel(kernelName);thisCamera = GetComponent<Camera>();if (!thisCamera){Debug.LogError("Object has no Camera");return;}CreateTextures();init = true;}protected void ClearTexture(ref RenderTexture textureToClear){if (null != textureToClear){textureToClear.Release();textureToClear = null;}}protected virtual void ClearTextures(){ClearTexture(ref output);ClearTexture(ref renderedSource);}protected void CreateTexture(ref RenderTexture textureToMake, int divide=1){textureToMake = new RenderTexture(texSize.x/divide, texSize.y/divide, 0);textureToMake.enableRandomWrite = true;textureToMake.Create();}protected virtual void CreateTextures(){}protected virtual void OnEnable(){Init();}protected virtual void OnDisable(){ClearTextures();init = false;}protected virtual void OnDestroy(){ClearTextures();init = false;}protected virtual void DispatchWithSource(ref RenderTexture source, ref RenderTexture destination){}protected void CheckResolution(out bool resChange ){resChange = false;}protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination){if (!init || shader == null){Graphics.Blit(source, destination);}else{CheckResolution(out _);DispatchWithSource(ref source, ref destination);}}}
初始化以后就會(huì)創(chuàng)建紋理,但是這個(gè)函數(shù)還沒(méi)有完善。
首先設(shè)置紋理的寬度和高度,然后獲取線(xiàn)程組的x和y的大小,這里GetKernelThreadGroupSizes的都三個(gè)參數(shù)out _表示忽略z
軸的線(xiàn)程組尺寸,這是 C# 語(yǔ)言中的一種方式,用于表示對(duì)某個(gè)輸出值不感興趣,不需要它的實(shí)際值。,然后創(chuàng)建兩張紋理并傳遞給shader。
protected virtual void CreateTextures(){texSize.x = thisCamera.pixelWidth;//返回?cái)z像機(jī)視口的寬度和高度texSize.y = thisCamera.pixelHeight;if (shader){uint x, y;shader.GetKernelThreadGroupSizes(kernelHandle, out x, out y,out _);groupSize.x = Mathf.CeilToInt((float)texSize.x / (float)x);groupSize.y = Mathf.CeilToInt((float)texSize.y / (float)y);}CreateTexture(ref output);CreateTexture(ref renderedSource);shader.SetTexture(kernelHandle, "source", renderedSource);shader.SetTexture(kernelHandle, "output", output);}
下面就是要獲取到渲染的紋理并傳遞到GPU
?OnRenderImage(RenderTexture source, RenderTexture destination)這個(gè)函數(shù)可以獲取攝像機(jī)渲染后的圖像到source,并通過(guò)對(duì)原圖像的一系列處理之后設(shè)置到destination上。
OnRenderImage
是 Unity 中的一個(gè)特殊回調(diào)函數(shù),用于在攝像機(jī)完成渲染之后對(duì)圖像進(jìn)行后處理。它是在攝像機(jī)渲染過(guò)程的最后階段被自動(dòng)調(diào)用的。你不需要手動(dòng)調(diào)用這個(gè)函數(shù),而是由 Unity 引擎在渲染管線(xiàn)中自動(dòng)調(diào)用。
protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination){if (!init || shader == null){Graphics.Blit(source, destination);}else{CheckResolution(out _);DispatchWithSource(ref source, ref destination);}}
接著我們繼續(xù)完善checkresolution和dispatchwithsource函數(shù)。
如果沒(méi)有設(shè)置texsize那么就執(zhí)行createtextures函數(shù)設(shè)置紋理大小
protected void CheckResolution(out bool resChange ){resChange = false;if (texSize.x != thisCamera.pixelWidth || texSize.y != thisCamera.pixelHeight){resChange = true;CreateTextures();}}
首先將源紋理傳遞到shader,然后給shader分配線(xiàn)程組,然后將shader處理后的紋理傳遞到destination
protected virtual void DispatchWithSource(ref RenderTexture source, ref RenderTexture destination){Graphics.Blit(source, renderedSource);shader.Dispatch(kernelHandle, groupSize.x, groupSize.y, 1);Graphics.Blit(output, destination);}
接著我們書(shū)寫(xiě)一個(gè)簡(jiǎn)單的后處理示例
我們只是在代碼中將原圖像的顏色乘上一個(gè)shade調(diào)整圖像的亮度
void Highlight(uint3 id : SV_DispatchThreadID)
{float4 srcColor = source[id.xy];float4 color =srcColor*shade;output[id.xy] = color;
}
效果展示:
接著我們想要讓角色周?chē)吡溜@示,但是其它地方仍然是變暗一些。
首先在cpu獲取角色的位置并且轉(zhuǎn)換到屏幕坐標(biāo),然后傳遞給shader。
protected override void OnRenderImage(RenderTexture source, RenderTexture destination){if (!init || shader == null){Graphics.Blit(source, destination);}else{if (trackedObject && thisCamera){Vector3 pos = thisCamera.WorldToScreenPoint(trackedObject.position);//將世界坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo)center.x = pos.x;center.y = pos.y;shader.SetVector("center", center);}bool resChange =false;CheckResolution(out _);if (resChange) SetProperties();DispatchWithSource(ref source, ref destination);}}
在shader中檢測(cè)像素是否在角色周?chē)?#xff0c;然后根據(jù)和角色的距離對(duì)顏色進(jìn)行插值。如果距離大于半徑完全是變暗的顏色,小于半徑就是高亮顯示。
float inCircle( float2 pt, float2 center, float radius, float edgeWidth ){float len = length(pt - center);return 1.0 - smoothstep(radius-edgeWidth, radius, len);
}[numthreads(8, 8, 1)]
void Highlight(uint3 id : SV_DispatchThreadID)
{float4 srcColor = source[id.xy];float4 shadedSrcColor =srcColor*shade;float4 highlight = inCircle((float2)id.xy,center.xy,radius,edgeWidth);float4 color =lerp(shadedSrcColor,srcColor,highlight);output[id.xy] = color;
}
效果: