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]);
    }
}

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

相关推荐
心疼你的一切2 小时前
Unity异步编程神器:Unitask库深度解析(功能+实战案例+API全指南)
深度学习·unity·c#·游戏引擎·unitask
呆呆敲代码的小Y4 小时前
【Unity 实用工具篇】 | Book Page Curl 快速实现翻书效果
游戏·unity·游戏引擎·u3d·免费游戏·翻书插件
AC梦17 小时前
unity中如何将UI上的字高清显示
ui·unity
小贺儿开发1 天前
Unity3D 智慧城市管理平台
数据库·人工智能·unity·智慧城市·数据可视化
June bug2 天前
【领域知识】休闲游戏一次发版全流程:Google Play + Apple App Store
unity
星夜泊客2 天前
C# 基础:为什么类可以在静态方法中创建自己的实例?
开发语言·经验分享·笔记·unity·c#·游戏引擎
dzj20212 天前
PointerEnter、PointerExit、PointerDown、PointerUp——鼠标点击物体,则开始旋转,鼠标离开或者松开物体,则停止旋转
unity·pointerdown·pointerup
心前阳光2 天前
Unity 模拟父子关系
android·unity·游戏引擎
在路上看风景2 天前
26. Mipmap
unity
咸鱼永不翻身2 天前
Unity视频资源压缩详解
unity·游戏引擎·音视频