unity手搓光线追踪渲染管线(SRP) 第四篇 材质影响反射、定向光、景深
·
一、材质影响反射
上篇我们做好了材质,我们要让它影响反射光,所以我们将追踪部分代码改为:
r.O = h.P + h.N * 1e-8f;
float mrp = lerp(0.04, 1.0, h.Mat.Metallic) * pow(1.0 - dot(h.R, h.N), 5);
if (rand() < mrp)
{
r.D = MetalReflect(r.D, h.N, h.Mat.Roughness);
}
else
{
r.D = DiffuseReflect(h.N);
}
接着将着色部分改为:
col1 *= h.Mat.Albedo;
col1 += h.Mat.Emission;
别急,我们慢慢讲。追踪部分,mrp是金属反射概率,用Fresnel-Schlick近似法改了一下,在Trace函数的最后记得加一句“h.R = -r.D;”;


二、定向光
我们定义定向光:
struct DirectionalLight
{
float3 Direction, Energy;
};
DirectionalLight newDirectionalLight(float3 d, float3 e)
{
DirectionalLight dl;
dl.Direction = d;
dl.Energy = e;
return dl;
}
在漫反射的时候我们考虑定向光,但在着色阶段怎么让它确定这次是漫反射还是金属反射,或是后面篇章我们要讲到的折射呢?我们在Hit结构里加一个数据来确定:
//0: MetalReflect
//1: DiffuseReflect
//2: Refract
uint NextState;
我们还要在它的构造函数中将它设置:
h.NextState = 0;
追踪阶段也要适配:
Hit h = Trace(r);
if (h.t < 1.#INF)
{
r.O = h.P + h.N * 1e-8f;
float mrp = lerp(0.04, 1.0, h.Mat.Metallic) * pow(1.0 - dot(h.R, h.N), 5);
if (rand() < mrp)
{
r.D = MetalReflect(r.D, h.N, h.Mat.Roughness);
h.NextState = 0;
}
else
{
r.D = DiffuseReflect(h.N);
h.NextState = 1;
}
hits[b] = h;
}
else
{
hitted = b;
hits[b] = h;
break;
}
同时将着色阶段改为:
Hit h = hits[hitted - c];
if (h.t < 1.#INF)
{
col1 *= h.Mat.Albedo;
if (h.NextState == 1)
{
DirectionalLight dl = newDirectionalLight(normalize(float3(1.0, 1.0, 1.0)), 10.0);
Ray lr = newRay(h.P, dl.Direction);
if (Trace(lr).t == 1.#INF)
{
col1 += dot(lr.D, h.N) * h.Mat.Albedo * dl.Energy / (4 * PI);
}
}
col1 += h.Mat.Emission;
}
else
{
col1 = float3(0.0, 0.5, 1.0);
}
其中我们暂时用着一个固定的光源,lr代表LightRay,我们还用了数学常数π,所以要在最开始定义这个静态常数。最后你会看到:

但是还有两个问题:你看那个立着的三角形的 背面,一圈一段的,而且这个阴影太硬了,现实中可不是这样:

可见,阴影是有过渡的。别急,我们一个一个说。
首先那一圈一圈的东西,是浮点数精度不足导致的,还记得上一篇的“r.O = h.P + h.N * 1e-8f;”那一句吗?现在我们又要用一下这个原理,但是我们这样改完还是有这样的情况,我们将偏移调大一点,改为1e-6f(一微米),效果不错:

接着就是阴影太硬的问题了,我们看一张图:

我猜想软阴影就是这样形成的,所以我们计算光照射线时给它一个偏移:
Ray lr = newRay(h.P + h.N * 1e-6f, normalize(dl.Direction + randSphere() * 0.01));
然后你会看到:

三、景深

像是这样,看到的图像是清晰的,如果物体到镜头的距离与焦距的差越大,物体就会越模糊,所以我们定义浮点值Aperture和FocusDistance,在RTRP.cs中设置它,然后修改函数CamRay并写一个它要使用的随机函数:
float2 randDisk()
{
return normalize(float2(rand(), rand()) * 2.0 - 1.0) * rand();
}
Ray CamRay(float2 uv)
{
float3 o = mul(CTW, float4(randDisk() * Aperture, 0.0, 1.0)).xyz;
float3 t = mul(CIP, float4(uv * 2.0 - 1.0, 0.0, 1.0)).xyz * FocusDistance;
t = mul(CTW, float4(t, 1.0)).xyz;
float3 d = normalize(t - o);
return newRay(o, d);
}
其中o代表原点,t代表焦平面上的对应点,d是方向。
最后你会看到:

更多推荐
所有评论(0)