一、材质影响反射

上篇我们做好了材质,我们要让它影响反射光,所以我们将追踪部分代码改为:

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是方向。

最后你会看到:

更多推荐