Unity阴影

阴影纹理判断深度那些我们都懂。我的疑惑是对于平行光,如何确定阴影贴图要覆盖的范围。

豆包回答:

左右 / 上下范围 :由 Shadow Distance 和主相机的视场角决定,以主相机为中心,向平行光方向扩展出一个覆盖主相机视锥在 Shadow Distance 内所有区域的立方体,确保主相机能看到的范围内,阴影都能被正确生成。

我感觉就是对场景的所有模型都判断深度,对于一个平行光方向,只用生成一次阴影纹理就能一直使用。如果转动光源,就会看见右下角有Global illumination processing,可能就是在重新生成ShadowMap。

豆包提供了这个脚本,用于标识平行光源阴影视锥。

cs 复制代码
using UnityEngine;
using UnityEditor;

[RequireComponent(typeof(Light))]
public class DirectionalShadowFrustumVisualizer : MonoBehaviour
{
    [Header("可视化设置")]
    [Tooltip("视锥线条颜色")]
    public Color frustumColor = Color.cyan;
    [Tooltip("线条宽度(仅Scene视图生效)")]
    public float lineWidth = 2f;

    private Light _dirLight;
    private Camera _mainCamera;

    // 视锥的8个顶点
    private Vector3[] _frustumCorners = new Vector3[8];

    private void OnEnable()
    {
        _dirLight = GetComponent<Light>();
        // 校验是否为平行光
        if (_dirLight.type != LightType.Directional)
        {
            Debug.LogError("该脚本仅支持平行光!");
            enabled = false;
            return;
        }
        _mainCamera = Camera.main;
    }

    // Scene视图绘制视锥
    private void OnDrawGizmos()
    {
        if (!_dirLight || !_mainCamera) return;

        // 获取全局Shadow Distance
        float shadowDistance = QualitySettings.shadowDistance;
        // 获取平行光的阴影近裁剪面
        float shadowNearPlane = _dirLight.shadowNearPlane;

        // 1. 计算主相机在Shadow Distance范围内的视锥投影边界
        CalculateMainCameraFrustumBounds(shadowDistance, out Vector3 camFrustumMin, out Vector3 camFrustumMax);

        // 2. 计算平行光虚拟相机的正交视锥参数
        Vector3 lightDir = _dirLight.transform.forward;
        // 视锥中心:主相机位置 + 平行光方向 * 阴影距离的一半
        Vector3 frustumCenter = _mainCamera.transform.position + lightDir * shadowDistance * 0.5f;
        // 视锥尺寸:基于主相机视锥的宽度、高度,以及阴影近/远裁剪面的距离
        Vector3 frustumSize = new Vector3(
            camFrustumMax.x - camFrustumMin.x,
            camFrustumMax.y - camFrustumMin.y,
            shadowDistance - shadowNearPlane
        );

        // 3. 计算视锥的8个顶点
        BuildFrustumCorners(frustumCenter, lightDir, frustumSize, shadowNearPlane);

        // 4. 绘制视锥线条
        Gizmos.color = frustumColor;
        Handles.color = frustumColor;
        //Handles.lineThickness = lineWidth;

        // 绘制视锥的12条边
        DrawLine(0, 1);
        DrawLine(1, 3);
        DrawLine(3, 2);
        DrawLine(2, 0);
        DrawLine(4, 5);
        DrawLine(5, 7);
        DrawLine(7, 6);
        DrawLine(6, 4);
        DrawLine(0, 4);
        DrawLine(1, 5);
        DrawLine(2, 6);
        DrawLine(3, 7);
    }

    // 计算主相机在指定距离下的视锥投影边界
    private void CalculateMainCameraFrustumBounds(float distance, out Vector3 min, out Vector3 max)
    {
        float halfFOV = _mainCamera.fieldOfView * 0.5f * Mathf.Deg2Rad;
        float aspect = _mainCamera.aspect;

        float halfHeight = Mathf.Tan(halfFOV) * distance;
        float halfWidth = halfHeight * aspect;

        min = new Vector3(-halfWidth, -halfHeight, distance);
        max = new Vector3(halfWidth, halfHeight, distance);

        // 转换到世界空间
        min = _mainCamera.transform.TransformVector(min);
        max = _mainCamera.transform.TransformVector(max);
    }

    // 构建正交视锥的8个顶点
    private void BuildFrustumCorners(Vector3 center, Vector3 lightDir, Vector3 size, float nearPlane)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 up = Vector3.up;
        Vector3 right = Vector3.Cross(lightDir, up).normalized;
        up = Vector3.Cross(right, lightDir).normalized;

        // 近裁剪面4个顶点
        _frustumCorners[0] = center - right * halfSize.x - up * halfSize.y - lightDir * (halfSize.z - nearPlane);
        _frustumCorners[1] = center + right * halfSize.x - up * halfSize.y - lightDir * (halfSize.z - nearPlane);
        _frustumCorners[2] = center - right * halfSize.x + up * halfSize.y - lightDir * (halfSize.z - nearPlane);
        _frustumCorners[3] = center + right * halfSize.x + up * halfSize.y - lightDir * (halfSize.z - nearPlane);

        // 远裁剪面4个顶点
        _frustumCorners[4] = center - right * halfSize.x - up * halfSize.y + lightDir * halfSize.z;
        _frustumCorners[5] = center + right * halfSize.x - up * halfSize.y + lightDir * halfSize.z;
        _frustumCorners[6] = center - right * halfSize.x + up * halfSize.y + lightDir * halfSize.z;
        _frustumCorners[7] = center + right * halfSize.x + up * halfSize.y + lightDir * halfSize.z;
    }

    // 绘制单条线段
    private void DrawLine(int indexA, int indexB)
    {
        Handles.DrawLine(_frustumCorners[indexA], _frustumCorners[indexB]);
    }
}

动了一下相机,视锥会跟着变化,规则挺复杂。

相关推荐
一只一只1 天前
Unity之Invoke
unity·游戏引擎·invoke
技术小甜甜1 天前
【Godot】【入门】信号系统从 0 到 1(UI/玩法彻底解耦的通用写法)
ui·游戏引擎·godot
技术小甜甜1 天前
【Godot】【入门】节点生命周期怎么用(避免帧循环乱写导致卡顿的范式)
游戏引擎·godot
tealcwu1 天前
【Unity踩坑】Simulate Touch Input From Mouse or Pen 导致检测不到鼠标点击和滚轮
unity·计算机外设·游戏引擎
ThreePointsHeat1 天前
Unity WebGL打包后启动方法,部署本地服务器
unity·游戏引擎·webgl
erxij1 天前
【游戏引擎之路】《古今东西4》正式立项——新的一年,开始长征
游戏引擎
迪普阳光开朗很健康1 天前
UnityScrcpy 可以让你在unity面板里玩手机的插件
unity·游戏引擎
陈言必行2 天前
Unity 之 设备性能分级与游戏画质设置与设备自动适配指南
游戏·unity·游戏引擎
CreasyChan2 天前
Unity DOTS技术栈详解
unity·c#·游戏引擎