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

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

相关推荐
dzj20218 小时前
Unity中使用LLMUnity遇到的问题(三)——如何配置和使用知识库
unity·llmunity·知识库大模型
Clank的游戏栈9 小时前
Unity自动化美术资源校验工具(模型/材质规范检测)技术详解
unity·自动化·材质
Sator116 小时前
Unity烘焙光打包后光照丢失问题
unity·光照贴图
Howrun77719 小时前
虚幻引擎_核心框架
游戏引擎·虚幻
GLDbalala1 天前
Unity 实现一个简单的构建机
unity·游戏引擎
JIes__2 天前
Unity(二)——Resources资源动态加载
unity·游戏引擎
地狱为王2 天前
Unity使用NovaSR将沉闷的16kHz音频升频成清晰的48kHz音频
unity·游戏引擎·音视频·novasr
dzj20212 天前
Unity中使用LLMUnity遇到的问题(二)——LLMUnity脚本学习和探索
unity·llmunity
警醒与鞭策2 天前
Cursor Agent Skill 原理及LLM , Agent, MCP ,Skill区别
android·unity·ai·cursor