2026-04-16 오브젝트 그림자
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using BadDog.Rendering.AreaLight;
|
||||
|
||||
namespace BadDog.Rendering.AreaLight.Samples
|
||||
{
|
||||
/// <summary>
|
||||
/// AreaLight Test Panel for runtime testing and debugging
|
||||
/// </summary>
|
||||
public class AreaLightTestPanel : MonoBehaviour
|
||||
{
|
||||
[Header("UI Settings")]
|
||||
[SerializeField] private Rect panelRect = new Rect(10, 10, 800, 500);
|
||||
|
||||
[Header("FPS Settings")]
|
||||
[SerializeField] private float fpsUpdateInterval = 0.5f;
|
||||
|
||||
// FPS tracking
|
||||
private float m_LastFpsUpdate;
|
||||
private int m_FrameCount;
|
||||
private float m_CurrentFps;
|
||||
|
||||
// GUI styles
|
||||
private GUIStyle m_WindowStyle;
|
||||
private GUIStyle m_LabelStyle;
|
||||
private GUIStyle m_ToggleStyle;
|
||||
private GUIStyle m_ButtonStyle;
|
||||
private bool m_StylesInitialized = false;
|
||||
|
||||
// Shadow atlas resolution options
|
||||
private static readonly int[] k_ShadowAtlasResolutions = { 256, 512, 1024, 2048, 4096, 8192 };
|
||||
private static readonly string[] k_ShadowAtlasResolutionLabels = { "256", "512", "1024", "2048", "4096", "8192" };
|
||||
private int m_ShadowAtlasResolutionIndex = 2; // Default 1024
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitializeFPSTracking();
|
||||
SetTargetFrameRate();
|
||||
InitializeShadowAtlasResolutionIndex();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateFPS();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
InitializeStyles();
|
||||
// Fixed window that cannot be dragged
|
||||
panelRect = GUILayout.Window(95279529, panelRect, DrawTestPanel, "AreaLight Test Panel", m_WindowStyle, GUILayout.Width(panelRect.width), GUILayout.Height(panelRect.height));
|
||||
}
|
||||
|
||||
private void InitializeFPSTracking()
|
||||
{
|
||||
m_LastFpsUpdate = Time.time;
|
||||
m_FrameCount = 0;
|
||||
m_CurrentFps = 0f;
|
||||
}
|
||||
|
||||
private void SetTargetFrameRate()
|
||||
{
|
||||
#if UNITY_ANDROID || UNITY_IOS
|
||||
// Set target frame rate to 60 for mobile platforms
|
||||
Application.targetFrameRate = 60;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void UpdateFPS()
|
||||
{
|
||||
m_FrameCount++;
|
||||
float currentTime = Time.time;
|
||||
|
||||
if (currentTime - m_LastFpsUpdate >= fpsUpdateInterval)
|
||||
{
|
||||
m_CurrentFps = m_FrameCount / (currentTime - m_LastFpsUpdate);
|
||||
m_FrameCount = 0;
|
||||
m_LastFpsUpdate = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeStyles()
|
||||
{
|
||||
if (m_StylesInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_WindowStyle = new GUIStyle(GUI.skin.window) { fontSize = 38 };
|
||||
m_LabelStyle = new GUIStyle(GUI.skin.label) { fontSize = 32, wordWrap = true };
|
||||
m_ToggleStyle = new GUIStyle(GUI.skin.toggle) { fontSize = 32 };
|
||||
m_ButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 28 };
|
||||
|
||||
m_StylesInitialized = true;
|
||||
}
|
||||
|
||||
private void InitializeShadowAtlasResolutionIndex()
|
||||
{
|
||||
var areaLightInstance = GetActiveAreaLightInstance();
|
||||
if (areaLightInstance != null)
|
||||
{
|
||||
var shadowSettings = GetShadowSettings(areaLightInstance);
|
||||
if (shadowSettings != null)
|
||||
{
|
||||
int currentResolution = shadowSettings.shadowAtlasResolution;
|
||||
for (int i = 0; i < k_ShadowAtlasResolutions.Length; i++)
|
||||
{
|
||||
if (k_ShadowAtlasResolutions[i] == currentResolution)
|
||||
{
|
||||
m_ShadowAtlasResolutionIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTestPanel(int windowID)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Space(10);
|
||||
|
||||
DrawPerformanceSection();
|
||||
DrawControlsSection();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawPerformanceSection()
|
||||
{
|
||||
GUILayout.Label("=== Performance ===", m_LabelStyle);
|
||||
|
||||
Color originalColor = GUI.contentColor;
|
||||
SetFPSColor();
|
||||
GUILayout.Label($"FPS: {m_CurrentFps:F1}", m_LabelStyle);
|
||||
GUI.contentColor = originalColor;
|
||||
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
|
||||
private void SetFPSColor()
|
||||
{
|
||||
if (m_CurrentFps >= 60f)
|
||||
{
|
||||
GUI.contentColor = Color.green;
|
||||
}
|
||||
else if (m_CurrentFps >= 30f)
|
||||
{
|
||||
GUI.contentColor = Color.yellow;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.contentColor = Color.red;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawControlsSection()
|
||||
{
|
||||
GUILayout.Label("=== AreaLight Controls ===", m_LabelStyle);
|
||||
|
||||
// Get AreaLight instance from URP asset
|
||||
var areaLightInstance = GetActiveAreaLightInstance();
|
||||
if (areaLightInstance == null)
|
||||
{
|
||||
GUILayout.Label("AreaLight Feature not found", m_LabelStyle);
|
||||
return;
|
||||
}
|
||||
|
||||
var lightSettings = GetLightSettings(areaLightInstance);
|
||||
var shadowSettings = GetShadowSettings(areaLightInstance);
|
||||
|
||||
if (lightSettings == null || shadowSettings == null)
|
||||
{
|
||||
GUILayout.Label("Failed to access AreaLight settings", m_LabelStyle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Light Settings
|
||||
GUILayout.Label("--- Light Settings ---", m_LabelStyle);
|
||||
DrawIntSlider("Max Area Lights", ref lightSettings.maxAreaLights, 0, 8);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Shadow Settings
|
||||
GUILayout.Label("--- Shadow Settings ---", m_LabelStyle);
|
||||
|
||||
// Shadow Atlas Resolution (sync index first)
|
||||
int currentResolution = shadowSettings.shadowAtlasResolution;
|
||||
for (int i = 0; i < k_ShadowAtlasResolutions.Length; i++)
|
||||
{
|
||||
if (k_ShadowAtlasResolutions[i] == currentResolution)
|
||||
{
|
||||
m_ShadowAtlasResolutionIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.Label("Shadow Atlas Resolution:", m_LabelStyle);
|
||||
int newIndex = GUILayout.SelectionGrid(m_ShadowAtlasResolutionIndex, k_ShadowAtlasResolutionLabels, 6, m_ButtonStyle);
|
||||
if (newIndex != m_ShadowAtlasResolutionIndex)
|
||||
{
|
||||
m_ShadowAtlasResolutionIndex = newIndex;
|
||||
shadowSettings.shadowAtlasResolution = k_ShadowAtlasResolutions[m_ShadowAtlasResolutionIndex];
|
||||
}
|
||||
|
||||
DrawIntSlider("Max Shadow Casting Lights", ref shadowSettings.maxShadowCastingLights, 0, 8);
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Shadow Filter (enum dropdown simulation)
|
||||
GUILayout.Label("Shadow Filter:", m_LabelStyle);
|
||||
string[] filterOptions = { "Off", "PCF2x2", "Tent5x5", "Tent7x7", "PCSS" };
|
||||
int currentFilterIndex = (int)shadowSettings.shadowFilter;
|
||||
int newFilterIndex = GUILayout.SelectionGrid(currentFilterIndex, filterOptions, 5, m_ButtonStyle);
|
||||
if (newFilterIndex != currentFilterIndex)
|
||||
{
|
||||
shadowSettings.shadowFilter = (AreaLightShadowSettings.BGAreaLightShadowMode)newFilterIndex;
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
// PCSS Settings (only show when PCSS is selected)
|
||||
if (shadowSettings.shadowFilter == AreaLightShadowSettings.BGAreaLightShadowMode.PCSS)
|
||||
{
|
||||
GUILayout.Label("--- PCSS Settings ---", m_LabelStyle);
|
||||
DrawSlider("PCSS Shadow Softness", ref shadowSettings.pcssShadowSoftness, 0f, 32f);
|
||||
DrawIntSlider("PCSS Blocker Sample Count", ref shadowSettings.pcssBlockerSampleCount, 4, 32);
|
||||
DrawIntSlider("PCSS Filter Sample Count", ref shadowSettings.pcssFilterSampleCount, 4, 32);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSlider(string label, ref float value, float min, float max)
|
||||
{
|
||||
GUILayout.Label($"{label}: {value:F2}", m_LabelStyle);
|
||||
// GUILayout.HorizontalSlider returns the current slider value
|
||||
// Ensure slider has enough width to be interactive
|
||||
value = GUILayout.HorizontalSlider(value, min, max, GUILayout.ExpandWidth(true), GUILayout.Height(20));
|
||||
}
|
||||
|
||||
private void DrawIntSlider(string label, ref int value, int min, int max)
|
||||
{
|
||||
GUILayout.Label($"{label}: {value}", m_LabelStyle);
|
||||
// Ensure slider has enough width to be interactive
|
||||
float newValue = GUILayout.HorizontalSlider(value, min, max, GUILayout.ExpandWidth(true), GUILayout.Height(20));
|
||||
int intValue = Mathf.RoundToInt(newValue);
|
||||
// Clamp to ensure value stays within bounds
|
||||
value = Mathf.Clamp(intValue, min, max);
|
||||
}
|
||||
|
||||
private AreaLighting GetActiveAreaLightInstance()
|
||||
{
|
||||
var urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
|
||||
if (urpAsset == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var scriptableRendererData = GetScriptableRendererData(urpAsset);
|
||||
if (scriptableRendererData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (scriptableRendererData.rendererFeatures != null)
|
||||
{
|
||||
foreach (var feature in scriptableRendererData.rendererFeatures)
|
||||
{
|
||||
if (feature is AreaLighting areaLightFeature && feature.isActive)
|
||||
{
|
||||
return areaLightFeature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ScriptableRendererData GetScriptableRendererData(UniversalRenderPipelineAsset urpAsset)
|
||||
{
|
||||
var property = typeof(UniversalRenderPipelineAsset).GetProperty("scriptableRendererData",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
return property.GetValue(urpAsset) as ScriptableRendererData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private AreaLightSettings GetLightSettings(AreaLighting areaLighting)
|
||||
{
|
||||
var field = typeof(AreaLighting).GetField("m_LightSettings",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
return field.GetValue(areaLighting) as AreaLightSettings;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private AreaLightShadowSettings GetShadowSettings(AreaLighting areaLighting)
|
||||
{
|
||||
var field = typeof(AreaLighting).GetField("m_ShadowSettings",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
return field.GetValue(areaLighting) as AreaLightShadowSettings;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1117998342ebd97458ec7e4d8eac1e84
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Samples/Scripts/AreaLightTestPanel.cs
|
||||
uploadId: 884030
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "BadDog.Rendering.AreaLight.Samples",
|
||||
"rootNamespace": "BadDog.Rendering.AreaLight.Samples",
|
||||
"references": [
|
||||
"Unity.RenderPipelines.Universal.Runtime",
|
||||
"BadDog.Rendering.AreaLight"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c9746ef8c6730b40ab7c1ea78d30b6b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Samples/Scripts/com.baddog.rendering.arealight.samples.asmdef
|
||||
uploadId: 884030
|
||||
Reference in New Issue
Block a user