版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/mobilebbki399/article/details/78359642
最近尝试实现了一下光线追踪体积光,效果如下:
光线追踪(Ray tracing)是三维计算机图形学中的特殊渲染算法,跟踪从眼睛发出的光线而不是光源发出的光线,通过这样一项技术生成编排好的场景的数学模型显现出来。(摘自维基百科)
实现步骤:
一、在灯光区域生成体积光的载体mesh,即我们的体积光实际上是渲染在mesh上的,因此光线追踪的起点是每个顶点的位置,并计算出该顶点的视线方向,延视线进行递进:
二、在灯光空间生成深度图渲染相机:
我们需要在射线递进的过程中通过比较深度来判断是否当前点处于光线不可到达的遮挡位置,以此来渲染体积光中的阴影位置,这里我渲染了视空间深度(当然如果考虑直接使用unity的阴影贴图也可以,这样就可以直接为unity灯光添加体积光效果了):
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
float depth : TEXCOORD0;
};
v2f vert(appdata_base v)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f, o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.depth = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return EncodeFloatRGBA(i.depth);
}
ENDCG
}
三、光线追踪:
为了在每次递进后可以方便的采样深度图,我将递进的过程放到了投影空间,并且在投影空间下可以方便的计算每次递进的距离:
for (float k = 0; k< RAY_STEP; k++) {
float4 curpos = beginPjPos;
float3 vdir = pjViewDir.xyz*k*delta;
curpos.xyz += vdir;
half cdep = LinearLightEyeDepth(-curpos.z);
float boardFac = step(-1, curpos.x)*step(-1, curpos.y)*step(-1, curpos.z)*step(curpos.x, 1)*step(curpos.y, 1)*step(curpos.z, 1);
curpos = ComputeScreenPos(curpos);
half2 pjuv = curpos.xy / curpos.w;
#if UNITY_UV_STARTS_AT_TOP
pjuv.y = 1 - pjuv.y;
#endif
#ifdef USE_COOKIE
fixed4 cookie = tex2D(internalCookie, pjuv);
fixed3 cookiecol = cookie.rgb*cookie.a;
#else
half2 toCent = pjuv - half2(0.5, 0.5);
half l = 1 - saturate((length(toCent) - 0.3) / (0.5 - 0.3));
fixed3 cookiecol = fixed3(l, l, l);
#endif
half dep = DecodeFloatRGBA(tex2D(internalShadowMap, pjuv)) / internalProjectionParams.w;
float shadow = step(cdep, dep) *(1 - saturate(cdep*internalProjectionParams.w));
col.rgb += cookiecol*i.vcol.rgb*delta / 2 * boardFac*shadow;
}
另外,考虑到在片段着色器下进行递进可能比较影响性能,可以尝试在顶点着色器下执行,注意如果在顶点着色器执行需要适当的增加载体mesh的顶点数量,否则可能无法达到较精确的效果,如下:
Demo GitHub链接请点击http://www.lsngo.net/2017/10/22/unityshader_volumetriclight/
更多文章:http://www.lsngo.net