日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

computeshader实现全局光照

發(fā)布時間:2023/12/31 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 computeshader实现全局光照 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

全局光照介紹

????????全局光照技術本身是一個很復雜的技術,有非常多方式實現(xiàn)。從管線來說有光柵化實現(xiàn)的全局光照,有光線追蹤實現(xiàn)的更逼真的行為樹。而我們當前主流的或者說更多人討論的是光柵化下的全局光照。

????????而全局光照又包括了更多的表現(xiàn),比如天氣,環(huán)境等都會影響到全局光照,而每一塊在引擎中可能都需要單獨處理。

????????當前流行的引擎比如unity用的光照貼圖、光照探針,反射平面等方式來模擬間接光照。而ue5用的ssgi+體素+有向距離場來做的間接光照。當前也有一些自己實現(xiàn)的間接光照效果。

????????最近對這塊也挺感興趣,參考了很多文章和論文,覺得可以用computeshader的gpu運算能力來實現(xiàn)一次全局實時光照效果。(當前還有一些限制,之后再優(yōu)化)

? ? ? ? 先看看沒有全局光照的效果

實現(xiàn)原理:

????????要得到全局光照,首先直接光照我們直接用lit算,其次間接光照我們需要考慮兩塊,一個是ao的信息,這里我直接用ssao的方式實現(xiàn),然后就是顏色信息,我們知道一個像素他會受到旁邊的多個物體或者像素的信息影響。還有一個就是在比較暗的環(huán)境下,要獲取到周圍的顏色信息來疊加。那么基于這幾點,我們需要的是全局的法線信息,明亮環(huán)境下的顏色信息,步進的方式實現(xiàn)顏色獲取和賦值來做間接光照。

????????我的方法其實還有很多空間,比如我的只有單次反射,而且我的高光沒有做,也是因為我在聯(lián)系全局光照的實現(xiàn)方式,實時全局光照還有其他更高效的方式實現(xiàn),比如體素,有號距離場等,后續(xù)再補上。

我的實現(xiàn)步驟如下:

1.創(chuàng)建可以渲染對象到rt的的renderfeature

2.渲染屏幕空間的法線信息

3.渲染屏幕空間的光亮顏色信息

4.全局光照計算

4.1.computeshader清理顏色信息

4.2.computeshader渲染間接光照

4.3.blit結合間接光照信息

1.創(chuàng)建可以渲染對象到rt的的renderfeature

????????為什么需要渲染信息到rt的renderfeature呢?首先是因為我們需要一些全局的信息,比如提到的全局法線信息以及光亮顏色信息。而我們的renderfeature是繼承renderobject的,是多渲染一個pass的object,是直接上色到cameracolortexture的,而我們是需要基于每個object渲染到我們自己希望的rt上的,所以就有了這個步驟。

????????這一步其實就是建立了一個繼承ScriptableRendererFeature的RenderObjectsToRT,然他創(chuàng)建一個繼承ScriptableRenderPass的RenderObjectsToRTPass。而這個RenderObjectsToRTPass也是針對每個object執(zhí)行渲染,但是我們修改了他的渲染目標rt

cmd.GetTemporaryRT(RT.id, RTResolution.x, RTResolution.y, 8, FilterMode.Bilinear, rtf); cmd.SetRenderTarget(RT.Identifier()); cmd.ClearRenderTarget(true, true, Color.black);

這樣再外面就能根據(jù)自己需要把物體渲染到對象上了。

像這里我定義passtag就是他的rt名。這樣shader中就可以直接用了。

2.渲染屏幕空間的法線信息

既然有了renderobjectstort的方式,就可以添加一個feature,這個feature的materail用我們normal的mat

然后shader中深度需要寫入,并且blend是OneZero

然后我們需要寫入他的世界空間法線信息

這樣得到的結果是

這就是我們需要的法線信息。

3.渲染屏幕空間的光亮顏色信息

上面有提到如果在場景比較暗的情況下我們要獲取場景的顏色信息就不能靠直接光照的顏色信息來組成顏色了,因為直接光照已經(jīng)在場景中沒有顏色信息了,是一篇死黑。這時候就需要我們提前拿到一個明亮的信息來給點光源照亮環(huán)境的機會。所以我們需要這樣的圖片。最終的效果在比較黑暗的情況下是這樣的:

可以看到他還是把點光源周圍的環(huán)境照亮了。

4.全局光照計算

接下來進入computeshader實現(xiàn)全局光照的效果。

4.1.computeshader清理顏色信息

首先肯定要清理我的rt中的顏色,不然會殘留上次的顏色。比較簡單:

uint2 uv = uint2(id.x, id.y); SDFRGBTexture[uv]= float4(0, 0, 0, 0); IndirectLightTexture[uv] = float4(0, 0, 0, 0);

這里兩個rt一個是用來現(xiàn)在點光源的光照信息的,一個是間接光照的信息。

4.2.computeshader渲染間接光照

這里應該是我們的渲染的重點。

首先我們會獲取深度坐標,然后跟uv一起去轉換裁剪空間的坐標到世界坐標,這里做法就比較傳統(tǒng)了,就是拿世界空間到裁剪空間的逆矩陣和我們的uv以及depth計算出來的。

float4 clipPos = float4(((float2(float(uv.x), float(uv.y)) / 1024.0000)) * 2.0 - 1.0, (1 - _CameraDepthAttachment[uv].r), 1); float4 posW = mul(_VPInvMatrix, clipPos); posW /= posW.w;

然后我們先運算ssao,他就是找當前像素點周圍圓環(huán)范圍內(nèi)的隨機點,最后跟他的距離成反比來得到這個ao值,也是迭代越多越精確了。

float3 v_s1 = PickSamplePoint(curPos, randAddon, i); //------------ao----------------- v_s1 *= sqrt((i + 1.0) * rcpSampleCount) * aoRADIUS; v_s1 = faceforward(v_s1, -normal, v_s1);float3 newPosAOW = posW + v_s1; float4 newScreenAOPos = mul(_VPMatrix, newPosAOW); newScreenAOPos /= newScreenAOPos.w; newScreenAOPos = (newScreenAOPos + float4(1, 1, 1, 1)) / 2; newScreenAOPos = float4(newScreenAOPos.x * _ScreenRect.x, newScreenAOPos.y * _ScreenRect.y, newScreenAOPos.z, newScreenAOPos.w); float4 newColorAOPos = ClipToWorld(uint2(newScreenAOPos.xy));//_RenderObjectsToRTFeature[uint2(newScreenAOPos.xy)] * 2000 - 1000; float3 v_s2_AO = newColorAOPos - posW;// Estimate the obscurance value float a1_AO = max(dot(v_s2_AO, normal), 0.0); float a2_AO = dot(v_s2_AO, v_s2_AO) + EPSILON; aoNum += a1_AO * rcp(a2_AO) * 2;//(_LevelID == 2 ? 2 : 2);

然后就是顏色部分了,也是在世界空間根據(jù)當前uv的位置來取方向步進一定的距離,然后拿到顏色再跟他的距離取反比來得到了。當前剛說了還得考慮完全黑的情況,所以會有一些黑暗情況下的顏色處理。然后要注意的一點是顏色一樣的就不要相加了,不然會導致整個界面很糊的樣子。

for (int i = 1; i <= sCount; i++){ #if defined(SHADER_API_D3D11)// This 'floor(1.0001 * s)' operation is needed to avoid a DX11 NVidia shader issue.i = floor(1.0001 * i); #endiffloat3 v_s1 = PickSamplePoint(curPos.xy, randAddon, i);//------------ao-----------------v_s1 *= sqrt((i + 1.0) * rcpSampleCount) * aoRADIUS;v_s1 = faceforward(v_s1, -normal, v_s1);float3 newPosAOW = curPos.xyz + v_s1;float4 newScreenAOPos = mul(_VPMatrix, float4(newPosAOW,1));newScreenAOPos /= newScreenAOPos.w;newScreenAOPos = (newScreenAOPos + float4(1.0, 1.0, 1.0, 1.0)) / 2.0;newScreenAOPos = float4(newScreenAOPos.x * _RTResolution.x, newScreenAOPos.y * _RTResolution.y, newScreenAOPos.x * _RTResolution.z, newScreenAOPos.y * _RTResolution.w);float4 newColorAOPos = ClipToWorld(uint2(newScreenAOPos.zw), newScreenAOPos.zw, 1);//_RenderObjectsToRTFeature[uint2(newScreenAOPos.xy)] * 2000 - 1000;float3 v_s2_AO = newColorAOPos.xyz - curPos.xyz;// Estimate the obscurance valuefloat a1_AO = max(dot(v_s2_AO, normal), 0.0);// *distance(_CameraPosition.xyz, posW) / 1000;float a2_AO = sqrt(dot(v_s2_AO, v_s2_AO)) + EPSILON;aoNum += a1_AO * rcp(a2_AO);//(_LevelID == 2 ? 2 : 2);v_s1 = PickSamplePoint(curPos.xy, randAddon, i);//------------ao-----------------// Make it distributed between [0, _Radius]v_s1 *= sqrt((i + 1.0) * rcpSampleCount) * RADIUS;v_s1 = faceforward(v_s1, -normal, v_s1);v_s1 = normalize(v_s1);/*uint isNextStep = 1;for (int j = 0; j < sCount; j++){if (dot(vS1floats[j], v_s1) > 0.95){isNextStep = 0;break;}}if (isNextStep == 0){continue;}vS1floats[i] = v_s1;*/for (int j = 1; j <= colNum; j+= 1){float3 newPosW = curPos.xyz + v_s1 * j;float4 newScreenPos = mul(_VPMatrix, float4(newPosW,1));newScreenPos /= newScreenPos.w;newScreenPos = (newScreenPos + float4(1, 1, 1, 1)) / 2;newScreenPos = float4(newScreenPos.x * _RTResolution.x, newScreenPos.y * _RTResolution.y, newScreenPos.z, newScreenPos.w);uint2 newUV = uint2(newScreenPos.xy);float4 newColorPos = float4(newPosW,1);//_RenderObjectsToRTFeature[newUV] * 2000 - 1000;float3 v_s2 = newColorPos.xyz - curPos.xyz;/*float vDistance = 0.1;if (abs(v_s1.y) < vDistance || abs(v_s1.x) < vDistance || abs(v_s1.z) < vDistance){continue;}*/float a2 = dot(v_s2, v_s2);if (a2 > 1){realAddNum++;float4 cameraColor = _CameraColorTexture[newUV];float3 colorValue = cameraColor.xyz - curColor.xyz;float colorLen = (colorValue.x * colorValue.x + colorValue.y * colorValue.y + colorValue.z * colorValue.z);//顏色相近就不加了if (colorLen < 0.5){continue;}float4 newColorNum = cameraColor;// *rcp(pow(2, _LevelID));if (cameraColor.r > 0.05 || cameraColor.g > 0.05 || cameraColor.b > 0.05){if (curColor.r < 0.01 && curColor.g < 0.01 && curColor.b < 0.01){newColorNum += onlyColor;// *((1 + materialProp) * 2);}}a2 += 1;color += newColorNum * rcp(colNum);//color = color / sqrt(a2);//rcp(a2 * (_LevelID == 0 ? 0.3 : (_LevelID == 1 ? 0.3 : 1.7)));// *((1 + materialProp) * 1);}}}color *= (realAddNum == 0) ? 1 : rcp(realAddNum);color *= 1 - color.a;color *= RADIUS;//color.rgb *= 20;aoNum *= aoRADIUS;// Apply contrastaoNum = PositivePow(aoNum * INTENSITY * rcpSampleCount, kContrast);color.a = 1 - aoNum;// Apply contrast//aoNum = PositivePow(aoNum * INTENSITY * rcpSampleCount, kContrast);IndirectLightTexture[uv] = color;

最后得到的效果是

這個就是間接光照的效果,可以看到他會產(chǎn)生很多鋸齒,如果就這樣展示整個界面還是有點問題的

可以看到鋸齒很明顯,那么我們就得抗鋸齒,抗鋸齒得方式很多,比如fxaa,taa等。我選擇的是直接模糊,因為他的消耗還能接受而且效果也不錯。

4.3.blit結合間接光照信息

最后我們肯定要把間接光照結合直接光一起展示,所以我再blit上做,當然如果有后處理,再那邊也要做一遍。

?

總結:

整體就是上面的思路。最后說一下我是建立了一個叫GlobalLight的ScriptableRendererFeature,再里面通過commandbuffer組織computeshader來實現(xiàn)的。

CommandBuffer cmd = CommandBufferPool.Get(profilerTag); using (new ProfilingScope(cmd, m_ProfilingSampler)) {cmd.Clear();Camera currentCamera = renderingData.cameraData.camera;var vpMatrix = currentCamera.projectionMatrix * currentCamera.worldToCameraMatrix;giComputeShader.SetMatrix(VPMatrixID, vpMatrix);giComputeShader.SetMatrix(VPInvMatrixID, vpMatrix.inverse);giComputeShader.SetVector(ScreenRectID, new Vector2(Screen.width, Screen.height));Vector3 screenLightPos = currentCamera.WorldToScreenPoint(AreaLight.areaLightPos);Vector3 prevScreenLightPos = currentCamera.WorldToScreenPoint(AreaLight.prevLightPos);giComputeShader.SetTexture(ClearKernel, IndirectLightTexturert, IndirectRT);giComputeShader.SetTexture(ClearKernel, SDFRGBTexturert, outputRGBRT);giComputeShader.SetVector(PrevScreenAreaLightPos, prevScreenLightPos);cmd.DispatchCompute(giComputeShader, ClearKernel, 32, 32, 1);giComputeShader.SetVector(ScreenAreaLightPos, screenLightPos);giComputeShader.SetVector(WorldLightPos, AreaLight.areaLightPos);giComputeShader.SetTexture(kernel, SDFRGBTexturert, outputRGBRT);cmd.SetComputeTextureParam(giComputeShader, kernel, cameraDepthTexture.id, cameraDepthTexture.Identifier());cmd.DispatchCompute(giComputeShader, kernel, 32, 32, 1);giComputeShader.SetTexture(IndirectLightkernel, IndirectLightTexturert, IndirectRT);cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, onlyColorRTFeature.id, onlyColorRTFeature.Identifier());cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, outputNormalRT.id, outputNormalRT.Identifier());cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, cameraOpaqueTexture.id, cameraOpaqueTexture.Identifier());cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, cameraDepthTexture.id, cameraDepthTexture.Identifier());cmd.SetComputeVectorParam(giComputeShader, cameraPos, currentCamera.transform.position);cmd.SetComputeIntParam(giComputeShader, qLevelID, (int)mEQLevel);cmd.SetComputeIntParam(giComputeShader, rayDistanceID, mRayDistance); cmd.DispatchCompute(giComputeShader, IndirectLightkernel, 32, 32, 1);{giComputeShader.SetTexture(RGBkernel, IndirectLightTexturert, IndirectRT);giComputeShader.SetTexture(RGBkernel, IndirectLightTexturert, IndirectRT);cmd.DispatchCompute(giComputeShader, RGBkernel, 32, 32, 1);}AreaLight.prevLightPos = AreaLight.areaLightPos;cmd.EndSample(profilerTag); }

總結

以上是生活随笔為你收集整理的computeshader实现全局光照的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。