体积光是透射的一种,次表面散射也是,所不同的是次表面散射是在模型里面,而体积光一般都是在模型外面。
伪体积光散射:
基于延迟渲染的体积光散射,简单说就是2D模拟3D效果,过程:
1.在屏幕中渲染光源
2.在屏幕中渲染遮蔽
3.屏幕中某点的颜色,是这个点到光源的这个线段上采样点的光的颜色的累积 (- -)。
#define NUM_SAMPLES 200 // 采样数
[numthreads( 16, 16, 1 )] //SV_GroupID 线程组的3D编号 最大只能是32X32
void VLS_CS( uint3 Gid : SV_GroupID, uint GI : SV_GroupIndex,uint3 DTid : SV_DispatchThreadID) //横
{
// 全局固定参数
float density = 1.0f;
float weight = 1.0f;
float decay = 0.995f;//0.98f;
float exposure = 1.0f/200;
matrix viewProject = mbMatrix[1];
float4 lightPos = float4(lbParam[0].xyz,1.0f);
float4 color = float4(0.0f,0.0f,0.0f,1.0f);
lightPos = mul(lightPos, viewProject); // pos 是对的
lightPos = lightPos / lightPos.w;
lightPos = (lightPos + 1.0f) / 2.0f;
lightPos.y = 1.0f - lightPos.y;
float2 texCoord = float2(DTid.x/800.0f,DTid.y/600.0f);
float2 deltaTexCoord = (texCoord - lightPos.xy);
deltaTexCoord *= 1.0f / NUM_SAMPLES * density;
float illuminationDecay = 5.0f;
for (uint i = 0; i < NUM_SAMPLES; i++)
{
texCoord -= deltaTexCoord;
uint2 tex = uint2(texCoord.x*800,texCoord.y*600);
float4 sampleColor = mTexture[tex];
sampleColor *= illuminationDecay * weight;
color += sampleColor;
illuminationDecay *= decay;
}
color *= exposure;
color.w = 1.0f;
Result[DTid.xy] = color;
}
这种方式渲染出的结果有一种光芒万丈的感觉,但是,缺点很明显光源必须在屏幕中。因为光不在屏幕中就采样不到光的颜色,这样是不行的。
体积光散射:
1.在NVIDIA SDK里面有一个体积光的例子,看起来应该具备通用的能力,但是巨复杂,而且从代码量看来看,效率肯定也不会太高。
2.intel的体积光的例子,貌似是用shadow map 不停的拍照,然后累积一张张切片,最终混合成体积光的样子。这种方式感觉比N卡的更废,尤其是在光源比较远的时候。
3.我自己的实现思路,与下面这张图类似,以eye到屏幕射出一条射线,然后在这个射线上进行采样,来判断是否被遮蔽。
这里的原理就是沿着深度的方向进行采样,看看有多少点在阴影里面,然后计算这个像素点最终的颜色。但是,这里有个问题,深度一般是[-1,1]这个采样应该如何把握呢?间隙太大,有些障碍物直接被忽略掉了。采样点太多肯定也是不行的。
演示: