Light and shadow

Created by miccall (转载请注明出处 miccall.tech)

1. Lighting

2. shadow

在执行玩剔除之后 ,就需要去渲染阴影贴图。
阴影贴图本质上是一个深度图,把摄像机放在灯光的位置来获取

首先 ,我们需要一个renderTexture 来生成一个阴影贴图


        // 生成一张阴影贴图 
        shadowMap = RenderTexture.GetTemporary(
            shadowMapSize, shadowMapSize, 16, RenderTextureFormat.Shadowmap
        );
        //将纹理的滤镜模式设置为双线性,并使用clamp
        shadowMap.filterMode = FilterMode.Bilinear;
        shadowMap.wrapMode = TextureWrapMode.Clamp;

之后,我们把这张图与commandbuffer绑定

// 渲染阴影之前,我们首先告诉GPU渲染到阴影贴图 
CoreUtils.SetRenderTarget(ShadowBuffer , shadowMap,RenderBufferLoadAction.DontCare,RenderBufferStoreAction.Store,ClearFlag.Depth);

现在就开始渲染纹理了


        // 开始渲染纹理 
        ShadowBuffer.BeginSample("Render Shadows");
        context.ExecuteCommandBuffer(ShadowBuffer);
        ShadowBuffer.Clear();

我们需要对场景中所有的灯,只要他的设置为阴影投射,那么我们就得为他指定一个深度图

for (var i = 0; i < _cull.visibleLights.Count; i++)
{
    // 超过最大的灯 
    if (i == maxVisibleLights) break;
    // 强度为 0 
    if (shadowData[i].x <= 0f) continue;
}

之后,我们需要得到相机位置的 V p 矩阵 unity有方法为我们计算


// V P 矩阵 viewMatrix and projectionMatrix 
if (!_cull.ComputeSpotShadowMatricesAndCullingPrimitives(
        i, out var viewMatrix, out var projectionMatrix, out var splitData
)) {
    //如果失败则跳过灯 将强度设置为零
    shadowData[i].x = 0f;
    continue;
}

之后,把得到的vp矩阵传入shader


// 设置矩阵 ,执行并清除
ShadowBuffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
ShadowBuffer.SetGlobalFloat(shadowBiasId, _cull.visibleLights[i].light.shadowBias);
context.ExecuteCommandBuffer(ShadowBuffer);
ShadowBuffer.Clear();


因为 dx 和 opengl 的 zbuffer 是反着的,所以我们还要处理一下

// 是否反转矩阵 
if (SystemInfo.usesReversedZBuffer) {
    projectionMatrix.m20 = -projectionMatrix.m20;
    projectionMatrix.m21 = -projectionMatrix.m21;
    projectionMatrix.m22 = -projectionMatrix.m22;
    projectionMatrix.m23 = -projectionMatrix.m23;
}