您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

1053 行
42 KiB

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Experimental.Rendering.OnTileDeferredRenderPipeline
{
class ShadowSetup : IDisposable
{
// shadow related stuff
const int k_MaxShadowDataSlots = 64;
const int k_MaxPayloadSlotsPerShadowData = 4;
ShadowmapBase[] m_Shadowmaps;
ShadowManager m_ShadowMgr;
ComputeBuffer s_ShadowDataBuffer;
ComputeBuffer s_ShadowPayloadBuffer;
public ShadowSetup(ShadowInitParameters shadowInit, ShadowSettings shadowSettings, out IShadowManager shadowManager)
{
s_ShadowDataBuffer = new ComputeBuffer(k_MaxShadowDataSlots, System.Runtime.InteropServices.Marshal.SizeOf(typeof(ShadowData)));
s_ShadowPayloadBuffer = new ComputeBuffer(k_MaxShadowDataSlots * k_MaxPayloadSlotsPerShadowData, System.Runtime.InteropServices.Marshal.SizeOf(typeof(ShadowPayload)));
ShadowAtlas.AtlasInit atlasInit;
atlasInit.baseInit.width = (uint)shadowInit.shadowAtlasWidth;
atlasInit.baseInit.height = (uint)shadowInit.shadowAtlasHeight;
atlasInit.baseInit.slices = 1;
atlasInit.baseInit.shadowmapBits = 32;
atlasInit.baseInit.shadowmapFormat = RenderTextureFormat.Shadowmap;
atlasInit.baseInit.samplerState = SamplerState.Default();
atlasInit.baseInit.comparisonSamplerState = ComparisonSamplerState.Default();
atlasInit.baseInit.clearColor = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
atlasInit.baseInit.maxPayloadCount = 0;
atlasInit.baseInit.shadowSupport = ShadowmapBase.ShadowSupport.Directional | ShadowmapBase.ShadowSupport.Point | ShadowmapBase.ShadowSupport.Spot;
atlasInit.shaderKeyword = null;
m_Shadowmaps = new ShadowmapBase[] { new ShadowAtlas(ref atlasInit) };
ShadowContext.SyncDel syncer = (ShadowContext sc) =>
{
// update buffers
uint offset, count;
ShadowData[] sds;
sc.GetShadowDatas(out sds, out offset, out count);
Debug.Assert(offset == 0);
s_ShadowDataBuffer.SetData(sds); // unfortunately we can't pass an offset or count to this function
ShadowPayload[] payloads;
sc.GetPayloads(out payloads, out offset, out count);
Debug.Assert(offset == 0);
s_ShadowPayloadBuffer.SetData(payloads);
};
// binding code. This needs to be in sync with ShadowContext.hlsl
ShadowContext.BindDel binder = (ShadowContext sc, CommandBuffer cb, ComputeShader computeShader, int computeKernel) =>
{
// bind buffers
cb.SetGlobalBuffer("_ShadowDatasExp", s_ShadowDataBuffer);
cb.SetGlobalBuffer("_ShadowPayloads", s_ShadowPayloadBuffer);
// bind textures
uint offset, count;
RenderTargetIdentifier[] tex;
sc.GetTex2DArrays(out tex, out offset, out count);
cb.SetGlobalTexture("_ShadowmapExp_PCF", tex[0]);
// TODO: Currently samplers are hard coded in ShadowContext.hlsl, so we can't really set them here
};
ShadowContext.CtxtInit scInit;
scInit.storage.maxShadowDataSlots = k_MaxShadowDataSlots;
scInit.storage.maxPayloadSlots = k_MaxShadowDataSlots * k_MaxPayloadSlotsPerShadowData;
scInit.storage.maxTex2DArraySlots = 1;
scInit.storage.maxTexCubeArraySlots = 0;
scInit.storage.maxComparisonSamplerSlots = 1;
scInit.storage.maxSamplerSlots = 0;
scInit.dataSyncer = syncer;
scInit.resourceBinder = binder;
m_ShadowMgr = new ShadowManager(shadowSettings, ref scInit, m_Shadowmaps);
// set global overrides - these need to match the override specified in ShadowDispatch.hlsl
m_ShadowMgr.SetGlobalShadowOverride( GPUShadowType.Point , ShadowAlgorithm.PCF, ShadowVariant.V1, ShadowPrecision.High, true );
m_ShadowMgr.SetGlobalShadowOverride( GPUShadowType.Spot , ShadowAlgorithm.PCF, ShadowVariant.V1, ShadowPrecision.High, true );
m_ShadowMgr.SetGlobalShadowOverride( GPUShadowType.Directional , ShadowAlgorithm.PCF, ShadowVariant.V1, ShadowPrecision.High, true );
shadowManager = m_ShadowMgr;
}
public void Dispose()
{
if (m_Shadowmaps != null)
{
(m_Shadowmaps[0] as ShadowAtlas).Dispose();
m_Shadowmaps = null;
}
m_ShadowMgr = null;
if (s_ShadowDataBuffer != null) {
s_ShadowDataBuffer.Release ();
s_ShadowDataBuffer = null;
}
if (s_ShadowPayloadBuffer != null) {
s_ShadowPayloadBuffer.Release ();
s_ShadowPayloadBuffer = null;
}
}
}
public class OnTileDeferredRenderPipelineInstance : RenderPipeline {
private readonly OnTileDeferredRenderPipeline m_Owner;
public OnTileDeferredRenderPipelineInstance(OnTileDeferredRenderPipeline owner)
{
m_Owner = owner;
if (m_Owner != null)
m_Owner.Build();
}
public override void Dispose()
{
base.Dispose();
if (m_Owner != null)
m_Owner.Cleanup();
}
public override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
{
base.Render(renderContext, cameras);
m_Owner.Render(renderContext, cameras);
}
}
[ExecuteInEditMode]
public class OnTileDeferredRenderPipeline : RenderPipelineAsset {
#if UNITY_EDITOR
[UnityEditor.MenuItem("RenderPipeline/OnTileDeferredPipeline/Create Pipeline Asset", false, 17)]
static void CreateDeferredRenderPipeline()
{
var instance = ScriptableObject.CreateInstance<OnTileDeferredRenderPipeline> ();
UnityEditor.AssetDatabase.CreateAsset (instance, "Assets/OnTileDeferredPipeline.asset");
}
[UnityEditor.MenuItem("RenderPipeline/OnTileDeferredPipeline/Material Upgraders/Upgrade Standard Shader Materials")]
static void SetupDeferredRenderPipelineMaterials()
{
Renderer[] _renderers = Component.FindObjectsOfType<Renderer> ();
foreach (Renderer _renderer in _renderers) {
Material[] _materials = _renderer.sharedMaterials;
foreach (Material _material in _materials) {
if (_material == null)
continue;
if (_material.shader.name.Contains ("Standard (Specular setup)")) {
_material.shader = Shader.Find("Standard-SRP (Specular setup)");
} else if (_material.shader.name.Contains ("Standard")) {
_material.shader = Shader.Find("Standard-SRP");
}
}
}
}
#endif
protected override IRenderPipeline InternalCreatePipeline()
{
return new OnTileDeferredRenderPipelineInstance(this);
}
[SerializeField] ShadowSettings m_ShadowSettings = new ShadowSettings();
ShadowSetup m_ShadowSetup;
IShadowManager m_ShadowMgr;
FrameId m_FrameId = new FrameId();
List<int> m_ShadowRequests = new List<int>();
Dictionary<int, int> m_ShadowIndices = new Dictionary<int,int>();
void InitShadowSystem(ShadowSettings shadowSettings)
{
m_ShadowSetup = new ShadowSetup(new ShadowInitParameters(), shadowSettings, out m_ShadowMgr);
}
void DeinitShadowSystem()
{
if (m_ShadowSetup != null)
{
m_ShadowSetup.Dispose();
m_ShadowSetup = null;
m_ShadowMgr = null;
}
}
// This must match MAX_LIGHTS in UnityStandardForwardMobile
const int k_MaxLights = 100;
const float FLT_MAX = float.PositiveInfinity;
const float FLT_MIN = float.NegativeInfinity;
// arrays for shader data
private Vector4[] m_LightData = new Vector4[k_MaxLights]; // x:Light_type, y:ShadowIndex z:w:UNUSED
private Vector4[] m_LightPositions = new Vector4[k_MaxLights];
private Vector4[] m_LightColors = new Vector4[k_MaxLights];
private Vector4[] m_LightDirections = new Vector4[k_MaxLights];
private Matrix4x4[] m_LightMatrix = new Matrix4x4[k_MaxLights];
private Matrix4x4[] m_WorldToLightMatrix = new Matrix4x4[k_MaxLights];
[SerializeField] TextureSettings m_TextureSettings = new TextureSettings();
[SerializeField] public bool UseLegacyCookies;
[SerializeField] public bool TransparencyShadows;
[SerializeField] public Mesh m_PointLightMesh;
[SerializeField] public float PointLightMeshScaleFactor = 2.0f;
[SerializeField] public Mesh m_SpotLightMesh;
[SerializeField] public float SpotLightMeshScaleFactor = 1.0f;
[SerializeField] public Mesh m_QuadMesh;
[SerializeField] public Mesh m_BoxMesh;
[SerializeField] public Texture m_DefaultSpotCookie;
[SerializeField] public Shader finalPassShader;
[SerializeField] public Shader deferredShader;
[SerializeField] public Shader deferredReflectionShader;
private TextureCache2D m_CookieTexArray;
private TextureCacheCubemap m_CubeCookieTexArray;
private TextureCacheCubemap m_CubeReflTexArray;
private ComputeBuffer s_LightDataBuffer;
private RenderPassAttachment s_GBufferAlbedo;
private RenderPassAttachment s_GBufferSpecRough;
private RenderPassAttachment s_GBufferNormal;
private RenderPassAttachment s_GBufferEmission;
private RenderPassAttachment s_CameraTarget;
private RenderPassAttachment s_Depth;
// write depth to red color buffer if on mobile so we can read it back
// cannot read depth buffer directly in shader on iOS
private RenderPassAttachment s_GBufferRedF32;
// TODO: When graphics/renderpass lands, replace code that uses boolean below with SystemInfo.supportsReadOnlyDepth
#if UNITY_EDITOR || UNITY_STANDALONE
static bool s_SupportsReadOnlyDepth = true;
#else
static bool s_SupportsReadOnlyDepth = false;
#endif
private static int _sceneViewBlitId;
private static int _sceneViewDepthId;
private static Material _blitDepthMaterial;
private Material m_DirectionalDeferredLightingMaterial;
private Material m_FiniteDeferredLightingMaterial;
private Material m_FiniteNearDeferredLightingMaterial;
private Material m_ReflectionMaterial;
private Material m_ReflectionNearClipMaterial;
private Material m_ReflectionNearAndFarClipMaterial;
private Material m_BlitMaterial;
private void OnValidate()
{
Build();
}
public void Cleanup()
{
if (m_BlitMaterial) DestroyImmediate(m_BlitMaterial);
if (m_DirectionalDeferredLightingMaterial) DestroyImmediate(m_DirectionalDeferredLightingMaterial);
if (m_FiniteDeferredLightingMaterial) DestroyImmediate(m_FiniteDeferredLightingMaterial);
if (m_FiniteNearDeferredLightingMaterial) DestroyImmediate(m_FiniteNearDeferredLightingMaterial);
if (m_ReflectionMaterial) DestroyImmediate (m_ReflectionMaterial);
if (m_ReflectionNearClipMaterial) DestroyImmediate (m_ReflectionNearClipMaterial);
if (m_ReflectionNearAndFarClipMaterial) DestroyImmediate (m_ReflectionNearAndFarClipMaterial);
if (s_LightDataBuffer != null) {
s_LightDataBuffer.Release ();
s_LightDataBuffer = null;
}
m_CookieTexArray.Release();
m_CubeCookieTexArray.Release();
m_CubeReflTexArray.Release();
DeinitShadowSystem();
}
public void Build()
{
s_GBufferAlbedo = new RenderPassAttachment(RenderTextureFormat.ARGB32) { hideFlags = HideFlags.HideAndDontSave };
s_GBufferSpecRough = new RenderPassAttachment(RenderTextureFormat.ARGB32) { hideFlags = HideFlags.HideAndDontSave };
s_GBufferNormal = new RenderPassAttachment(RenderTextureFormat.ARGB2101010) { hideFlags = HideFlags.HideAndDontSave };
s_GBufferEmission = new RenderPassAttachment(RenderTextureFormat.ARGBHalf) { hideFlags = HideFlags.HideAndDontSave };
s_Depth = new RenderPassAttachment(RenderTextureFormat.Depth) { hideFlags = HideFlags.HideAndDontSave };
s_CameraTarget = s_GBufferAlbedo;
s_GBufferEmission.Clear(new Color(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0);
s_Depth.Clear(new Color(), 1.0f, 0);
if (s_SupportsReadOnlyDepth)
{
s_GBufferRedF32 = null;
}
else
{
s_GBufferRedF32 = new RenderPassAttachment(RenderTextureFormat.RFloat) { hideFlags = HideFlags.HideAndDontSave };
s_GBufferRedF32.Clear(new Color(), 1.0f, 0);
}
m_BlitMaterial = new Material (finalPassShader) { hideFlags = HideFlags.HideAndDontSave };
_blitDepthMaterial = new Material(Shader.Find("Hidden/BlitCopyWithDepth")) { hideFlags = HideFlags.HideAndDontSave };
_sceneViewBlitId = Shader.PropertyToID("_TempCameraRT");
_sceneViewDepthId = Shader.PropertyToID("_TempCameraDepth");
m_DirectionalDeferredLightingMaterial = new Material (deferredShader) { hideFlags = HideFlags.HideAndDontSave };
m_DirectionalDeferredLightingMaterial.SetInt("_SrcBlend", (int)BlendMode.One);
m_DirectionalDeferredLightingMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_DirectionalDeferredLightingMaterial.SetInt("_SrcABlend", (int)BlendMode.One);
m_DirectionalDeferredLightingMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_DirectionalDeferredLightingMaterial.SetInt("_CullMode", (int)CullMode.Off);
m_DirectionalDeferredLightingMaterial.SetInt("_CompareFunc", (int)CompareFunction.Always);
m_FiniteDeferredLightingMaterial = new Material (deferredShader) { hideFlags = HideFlags.HideAndDontSave };
m_FiniteDeferredLightingMaterial.SetInt("_SrcBlend", (int)BlendMode.One);
m_FiniteDeferredLightingMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_FiniteDeferredLightingMaterial.SetInt("_SrcABlend", (int)BlendMode.One);
m_FiniteDeferredLightingMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_FiniteDeferredLightingMaterial.SetInt("_CullMode", (int)CullMode.Back);
m_FiniteDeferredLightingMaterial.SetInt("_CompareFunc", (int)CompareFunction.LessEqual);
m_FiniteNearDeferredLightingMaterial = new Material (deferredShader) { hideFlags = HideFlags.HideAndDontSave };
m_FiniteNearDeferredLightingMaterial.SetInt("_SrcBlend", (int)BlendMode.One);
m_FiniteNearDeferredLightingMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_FiniteNearDeferredLightingMaterial.SetInt("_SrcABlend", (int)BlendMode.One);
m_FiniteNearDeferredLightingMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_FiniteNearDeferredLightingMaterial.SetInt("_CullMode", (int)CullMode.Front);
m_FiniteNearDeferredLightingMaterial.SetInt("_CompareFunc", (int)CompareFunction.Greater);
m_ReflectionMaterial = new Material (deferredReflectionShader) { hideFlags = HideFlags.HideAndDontSave };
m_ReflectionMaterial.SetInt("_SrcBlend", (int)BlendMode.DstAlpha);
m_ReflectionMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_ReflectionMaterial.SetInt("_SrcABlend", (int)BlendMode.DstAlpha);
m_ReflectionMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_ReflectionMaterial.SetInt("_CullMode", (int)CullMode.Back);
m_ReflectionMaterial.SetInt("_CompareFunc", (int)CompareFunction.LessEqual);
m_ReflectionNearClipMaterial = new Material (deferredReflectionShader) { hideFlags = HideFlags.HideAndDontSave };
m_ReflectionNearClipMaterial.SetInt("_SrcBlend", (int)BlendMode.DstAlpha);
m_ReflectionNearClipMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_ReflectionNearClipMaterial.SetInt("_SrcABlend", (int)BlendMode.DstAlpha);
m_ReflectionNearClipMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_ReflectionNearClipMaterial.SetInt("_CullMode", (int)CullMode.Front);
m_ReflectionNearClipMaterial.SetInt("_CompareFunc", (int)CompareFunction.GreaterEqual);
m_ReflectionNearAndFarClipMaterial = new Material (deferredReflectionShader) { hideFlags = HideFlags.HideAndDontSave };
m_ReflectionNearAndFarClipMaterial.SetInt("_SrcBlend", (int)BlendMode.DstAlpha);
m_ReflectionNearAndFarClipMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_ReflectionNearAndFarClipMaterial.SetInt("_SrcABlend", (int)BlendMode.DstAlpha);
m_ReflectionNearAndFarClipMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_ReflectionNearAndFarClipMaterial.SetInt("_CullMode", (int)CullMode.Off);
m_ReflectionNearAndFarClipMaterial.SetInt("_CompareFunc", (int)CompareFunction.Always);
m_CookieTexArray = new TextureCache2D();
m_CubeCookieTexArray = new TextureCacheCubemap();
m_CubeReflTexArray = new TextureCacheCubemap();
m_CookieTexArray.AllocTextureArray(8, m_TextureSettings.spotCookieSize, m_TextureSettings.spotCookieSize, TextureFormat.RGBA32, true);
m_CubeCookieTexArray.AllocTextureArray(4, m_TextureSettings.pointCookieSize, TextureFormat.RGBA32, true);
m_CubeReflTexArray.AllocTextureArray(64, m_TextureSettings.reflectionCubemapSize, TextureCache.GetPreferredHdrCompressedTextureFormat, true);
s_LightDataBuffer = new ComputeBuffer(k_MaxLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(SFiniteLightData)));
//shadows
InitShadowSystem(m_ShadowSettings);
}
void NewFrame()
{
// update texture caches
m_CookieTexArray.NewFrame();
m_CubeCookieTexArray.NewFrame();
m_CubeReflTexArray.NewFrame();
}
public void Render(ScriptableRenderContext context, IEnumerable<Camera> cameras)
{
foreach (var camera in cameras) {
// Culling
ScriptableCullingParameters cullingParams;
if (!CullResults.GetCullingParameters (camera, out cullingParams))
continue;
m_ShadowMgr.UpdateCullingParameters(ref cullingParams);
var cullResults = CullResults.Cull (ref cullingParams, context);
NewFrame ();
UpdateShadowConstants (camera, cullResults);
CommandBuffer cmdShadow = CommandBufferPool.Get();
m_ShadowMgr.RenderShadows( m_FrameId, context, cmdShadow, cullResults, cullResults.visibleLights );
m_ShadowMgr.SyncData();
m_ShadowMgr.BindResources( cmdShadow, null, 0 );
context.ExecuteCommandBuffer(cmdShadow);
CommandBufferPool.Release(cmdShadow);
context.SetupCameraProperties(camera);
// The scene view needs extra blit because it'll be using a non-fullscreen viewport
if (camera.cameraType == CameraType.SceneView)
{
using (var cmd = new CommandBuffer())
{
cmd.GetTemporaryRT(_sceneViewBlitId, camera.pixelWidth, camera.pixelHeight, 0,
FilterMode.Point, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default);
cmd.GetTemporaryRT(_sceneViewDepthId, camera.pixelWidth, camera.pixelHeight, 24,
FilterMode.Point, RenderTextureFormat.Depth, RenderTextureReadWrite.Default);
context.ExecuteCommandBuffer(cmd);
}
s_CameraTarget.BindSurface(new RenderTargetIdentifier(_sceneViewBlitId), false, true);
s_Depth.BindSurface(new RenderTargetIdentifier(_sceneViewDepthId), false, false);
}
else
{
s_CameraTarget.BindSurface(BuiltinRenderTextureType.CameraTarget, false, true);
s_Depth.BindSurface(BuiltinRenderTextureType.Depth, false, false);
}
// set load store actions
s_GBufferSpecRough.BindSurface (BuiltinRenderTextureType.None, false, false);
s_GBufferNormal.BindSurface (BuiltinRenderTextureType.None, false, false);
s_GBufferEmission.BindSurface (BuiltinRenderTextureType.None, false, false);
if (s_GBufferRedF32 != null)
s_GBufferRedF32.BindSurface(BuiltinRenderTextureType.None, false, false);
ExecuteRenderLoop(camera, cullResults, context);
if (camera.cameraType == CameraType.SceneView)
{
using (var cmd = new CommandBuffer())
{
cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);
cmd.SetGlobalTexture("_DepthTex", _sceneViewDepthId);
cmd.Blit(_sceneViewBlitId, BuiltinRenderTextureType.CameraTarget, _blitDepthMaterial);
context.ExecuteCommandBuffer(cmd);
}
}
context.Submit ();
}
}
void ExecuteRenderLoop(Camera camera, CullResults cullResults, ScriptableRenderContext loop)
{
using (var rp = new RenderPass (loop, camera.pixelWidth, camera.pixelHeight, 1, s_SupportsReadOnlyDepth ?
new[] { s_GBufferAlbedo, s_GBufferSpecRough, s_GBufferNormal, s_GBufferEmission } :
new[] { s_GBufferAlbedo, s_GBufferSpecRough, s_GBufferNormal, s_GBufferEmission, s_GBufferRedF32 }, s_Depth)) {
// GBuffer pass
using (new RenderPass.SubPass (rp, s_SupportsReadOnlyDepth ?
new[] { s_GBufferAlbedo, s_GBufferSpecRough, s_GBufferNormal, s_GBufferEmission } :
new[] { s_GBufferAlbedo, s_GBufferSpecRough, s_GBufferNormal, s_GBufferEmission, s_GBufferRedF32 }, null)) {
using (var cmd = new CommandBuffer { name = "Create G-Buffer" }) {
cmd.EnableShaderKeyword ("UNITY_HDR_ON");
cmd.ClearRenderTarget(true, true, new Color(0, 0, 0, 0));
loop.ExecuteCommandBuffer (cmd);
// render opaque objects using Deferred pass
var drawSettings = new DrawRendererSettings (camera, new ShaderPassName ("Deferred")) {
sorting = { flags = SortFlags.CommonOpaque },
rendererConfiguration = RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbe
};
var filterSettings = new FilterRenderersSettings(true) {renderQueueRange = RenderQueueRange.opaque};
loop.DrawRenderers (cullResults.visibleRenderers, ref drawSettings, filterSettings);
}
}
//Lighting Pass
using (new RenderPass.SubPass(rp, new[] { s_GBufferEmission },
new[] { s_GBufferAlbedo, s_GBufferSpecRough, s_GBufferNormal, s_SupportsReadOnlyDepth ? s_Depth : s_GBufferRedF32 }, true))
{
using (var cmd = new CommandBuffer { name = "Deferred Lighting and Reflections Pass"} )
{
RenderLightsDeferred (cullResults, cmd);
RenderReflections (camera, cmd, cullResults);
loop.ExecuteCommandBuffer(cmd);
}
}
//skybox
using (new RenderPass.SubPass (rp, new[] { s_GBufferEmission }, null)) {
loop.DrawSkybox (camera);
}
//Single Pass Forward Transparencies
using (new RenderPass.SubPass(rp, new[] { s_GBufferEmission }, null))
{
using (var cmd = new CommandBuffer { name = "Forwward Lighting Setup"} )
{
SetupLightShaderVariables (cullResults, camera, cmd);
loop.ExecuteCommandBuffer(cmd);
var settings = new DrawRendererSettings(camera, new ShaderPassName("ForwardSinglePass"))
{
sorting = { flags = SortFlags.CommonTransparent },
rendererConfiguration = RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbe,
};
var filterSettings = new FilterRenderersSettings(true) {renderQueueRange = RenderQueueRange.transparent};
loop.DrawRenderers (cullResults.visibleRenderers, ref settings, filterSettings);
}
}
//Final pass
using (new RenderPass.SubPass(rp, new[] { s_CameraTarget }, new[] { s_GBufferEmission }))
{
var cmd = new CommandBuffer { name = "FinalPass" };
cmd.DrawProcedural(new Matrix4x4(), m_BlitMaterial, 0, MeshTopology.Triangles, 3);
loop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
}
}
// Utilites
static Matrix4x4 GetFlipMatrix()
{
Matrix4x4 flip = Matrix4x4.identity;
bool isLeftHand = (LightDefinitions.USE_LEFTHAND_CAMERASPACE) != 0;
if (isLeftHand) flip.SetColumn(2, new Vector4(0.0f, 0.0f, -1.0f, 0.0f));
return flip;
}
static Matrix4x4 WorldToCamera(Camera camera)
{
return GetFlipMatrix() * camera.worldToCameraMatrix;
}
static Matrix4x4 CameraToWorld(Camera camera)
{
return camera.cameraToWorldMatrix * GetFlipMatrix();
}
static Matrix4x4 CameraProjection(Camera camera)
{
return camera.projectionMatrix * GetFlipMatrix();
}
Matrix4x4 PerspectiveCotanMatrix(float cotangent, float zNear, float zFar )
{
float deltaZ = zNear - zFar;
var m = Matrix4x4.zero;
m.m00 = cotangent; m.m01 = 0.0f; m.m02 = 0.0f; m.m03 = 0.0f;
m.m10 = 0.0f; m.m11 = cotangent; m.m12 = 0.0f; m.m13 = 0.0f;
m.m20 = 0.0f; m.m21 = 0.0f; m.m22 = (zFar + zNear) / deltaZ; m.m23 = 2.0f * zNear * zFar / deltaZ;
m.m30 = 0.0f; m.m31 = 0.0f; m.m32 = -1.0f; m.m33 = 0.0f;
return m;
}
float GetCotanHalfSpotAngle (float spotAngle)
{
const float degToRad = (float)(Mathf.PI / 180.0);
var cs = Mathf.Cos(0.5f * spotAngle * degToRad);
var ss = Mathf.Sin(0.5f * spotAngle * degToRad);
return cs / ss; //cothalfspotangle
}
// Shadows
void UpdateShadowConstants(Camera camera, CullResults inputs)
{
m_FrameId.frameCount++;
// get the indices for all lights that want to have shadows
m_ShadowRequests.Clear();
m_ShadowRequests.Capacity = inputs.visibleLights.Count;
int lcnt = inputs.visibleLights.Count;
for (int i = 0; i < lcnt; ++i)
{
VisibleLight vl = inputs.visibleLights[i];
AdditionalShadowData asd = vl.light.GetComponent<AdditionalShadowData>();
if (vl.light.shadows != LightShadows.None && asd != null && asd.shadowDimmer > 0.0f)
m_ShadowRequests.Add(i);
}
// pass this list to a routine that assigns shadows based on some heuristic
uint shadowRequestCount = (uint)m_ShadowRequests.Count;
int[] shadowRequests = m_ShadowRequests.ToArray();
int[] shadowDataIndices;
m_ShadowMgr.ProcessShadowRequests(m_FrameId, inputs, camera, false, inputs.visibleLights,
ref shadowRequestCount, shadowRequests, out shadowDataIndices);
// update the visibleLights with the shadow information
m_ShadowIndices.Clear();
for (uint i = 0; i < shadowRequestCount; i++)
{
m_ShadowIndices.Add(shadowRequests[i], shadowDataIndices[i]);
}
}
// Reflections
void RenderReflections(Camera camera, CommandBuffer cmd, CullResults cullResults)
{
var probes = cullResults.visibleReflectionProbes;
var worldToView = camera.worldToCameraMatrix; //WorldToCamera(camera);
// matches builtin deferred
float nearDistanceFudged = camera.nearClipPlane * 1.001f;
float farDistanceFudged = camera.farClipPlane * 0.999f;
var viewDir = camera.cameraToWorldMatrix.GetColumn(2);
var viewDirNormalized = -1 * Vector3.Normalize(new Vector3 (viewDir.x, viewDir.y, viewDir.z));
var eyePlane = new Plane ();
eyePlane.SetNormalAndPosition(viewDirNormalized, camera.transform.position);
// Note: Optimization for tiled GPUs: render all probes in reverse order so they are blended into the existing emission buffer with the correct blend settings as follows:
// emisNew = emis + Lerp( Lerp( Lerp(base,probe0,1-t0), probe1, 1-t1 ), probe2, 1-t2)....
// DST_COL = DST_COL + DST_ALPHA * SRC_COLOR
// DST_ALPHA = DST_ALPHA * SRC_ALPHA
int numProbes = probes.Count;
for (int i = numProbes-1; i >= 0; i--)
{
var rl = probes [i];
var cubemap = rl.texture;
// always a box for now
if (cubemap == null)
continue;
var bnds = rl.bounds;
var boxOffset = rl.center; // reflection volume offset relative to cube map capture point
var blendDistance = rl.blendDistance;
// TODO: fix for rotations on probes... Builtin Unity also does not take these into account, for now just grab position for mat
//var mat = rl.localToWorld;
Matrix4x4 mat = Matrix4x4.identity;
mat.SetColumn (3, rl.localToWorld.GetColumn (3));
var boxProj = (rl.boxProjection != 0);
var probePosition = mat.GetColumn (3); // translation vector
var probePosition1 = new Vector4 (probePosition [0], probePosition [1], probePosition [2], boxProj ? 1f : 0f);
// C is reflection volume center in world space (NOT same as cube map capture point)
var e = bnds.extents; // 0.5f * Vector3.Max(-boxSizes[p], boxSizes[p]);
var combinedExtent = e + new Vector3(blendDistance, blendDistance, blendDistance);
Matrix4x4 scaled = Matrix4x4.Scale (combinedExtent * 2.0f);
mat = mat * Matrix4x4.Translate (boxOffset) * scaled;
var probeRadius = combinedExtent.magnitude;
var viewDistance = eyePlane.GetDistanceToPoint(boxOffset);
bool intersectsNear = viewDistance - probeRadius <= nearDistanceFudged;
bool intersectsFar = viewDistance + probeRadius >= farDistanceFudged;
bool renderAsQuad = (intersectsNear && intersectsFar);
var props = new MaterialPropertyBlock ();
props.SetFloat ("_LightAsQuad", renderAsQuad ? 1 : 0);
var min = rl.bounds.min;
var max = rl.bounds.max;
// TODO: (cleanup) dont use builtins like unity_SpecCube0
cmd.SetGlobalTexture("unity_SpecCube0", cubemap);
cmd.SetGlobalVector("unity_SpecCube0_HDR", rl.probe.textureHDRDecodeValues);
cmd.SetGlobalVector ("unity_SpecCube0_BoxMin", min);
cmd.SetGlobalVector ("unity_SpecCube0_BoxMax", max);
cmd.SetGlobalVector ("unity_SpecCube0_ProbePosition", probePosition1);
cmd.SetGlobalVector ("unity_SpecCube1_ProbePosition", new Vector4(0, 0, 0, blendDistance));
if (renderAsQuad) {
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_ReflectionNearAndFarClipMaterial, 0, 0, props);
} else if (intersectsNear) {
cmd.DrawMesh (m_BoxMesh, mat, m_ReflectionNearClipMaterial, 0, 0, props);
} else{
cmd.DrawMesh (m_BoxMesh, mat, m_ReflectionMaterial, 0, 0, props);
}
}
// draw the base probe
// TODO: (cleanup) dont use builtins like unity_SpecCube0
{
var props = new MaterialPropertyBlock ();
props.SetFloat ("_LightAsQuad", 1.0f);
// base reflection probe
var topCube = ReflectionProbe.defaultTexture;
var defdecode = ReflectionProbe.defaultTextureHDRDecodeValues;
cmd.SetGlobalTexture ("unity_SpecCube0", topCube);
cmd.SetGlobalVector ("unity_SpecCube0_HDR", defdecode);
cmd.SetGlobalVector("unity_SpecCube0_BoxMin", new Vector4(FLT_MIN, FLT_MIN, FLT_MIN, 1));
cmd.SetGlobalVector("unity_SpecCube0_BoxMax", new Vector4(FLT_MAX, FLT_MAX, FLT_MAX, 1));
cmd.SetGlobalVector ("unity_SpecCube0_ProbePosition", new Vector4 (0.0f, 0.0f, 0.0f, 0.0f));
cmd.SetGlobalVector ("unity_SpecCube1_ProbePosition", new Vector4 (0.0f, 0.0f, 0.0f, 1.0f));
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_ReflectionNearAndFarClipMaterial, 0, 0, props);
}
}
Matrix4x4 SpotlightMatrix (Matrix4x4 worldToLight, float range, float chsa)
{
Matrix4x4 temp1 = Matrix4x4.Scale(new Vector3 (-.5f, -.5f, 1.0f));
Matrix4x4 temp2 = Matrix4x4.Translate( new Vector3 (.5f, .5f, 0.0f));
Matrix4x4 temp3 = PerspectiveCotanMatrix (chsa, 0.0f, range);
return temp2 * temp1 * temp3 * worldToLight;
}
void RenderSpotlight(VisibleLight light, CommandBuffer cmd, MaterialPropertyBlock properties, bool renderAsQuad, bool intersectsNear)
{
float range = light.range;
var lightToWorld = light.localToWorld;
var worldToLight = lightToWorld.inverse;
float chsa = GetCotanHalfSpotAngle (light.spotAngle);
// Setup Light Matrix
properties.SetMatrix ("_LightMatrix0", SpotlightMatrix(worldToLight, range, chsa));
// Setup Spot Rendering mesh matrix
float sideLength = range / chsa;
// scalingFactor corrosoponds to the scale factor setting (and wether file scale is used) of mesh in Unity mesh inspector.
// A scale factor setting in Unity of 0.01 would require this to be set to 100. A scale factor setting of 1, is just 1 here.
lightToWorld = lightToWorld * Matrix4x4.Scale (new Vector3(sideLength*SpotLightMeshScaleFactor, sideLength*SpotLightMeshScaleFactor, range*SpotLightMeshScaleFactor));
//set default cookie for spot light if there wasnt one added to the light manually
Texture cookie = light.light.cookie;
if (cookie == null) {
cmd.SetGlobalTexture ("_LightTexture0", m_DefaultSpotCookie);
}
// turn on spotlights in shader, there is no spot cookie varient so no need for that
cmd.EnableShaderKeyword ("SPOT");
if (renderAsQuad) {
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DirectionalDeferredLightingMaterial, 0, 0, properties);
} else if (intersectsNear) {
cmd.DrawMesh (m_SpotLightMesh, lightToWorld, m_FiniteNearDeferredLightingMaterial, 0, 0, properties);
} else {
cmd.DrawMesh (m_SpotLightMesh, lightToWorld, m_FiniteDeferredLightingMaterial, 0, 0, properties);
}
}
void RenderPointLight(VisibleLight light, CommandBuffer cmd, MaterialPropertyBlock properties, bool renderAsQuad, bool intersectsNear)
{
Vector3 lightPos = light.localToWorld.GetColumn (3); //position
float range = light.range;
// scalingFactor corrosoponds to the scale factor setting (and wether file scale is used) of mesh in Unity mesh inspector.
// A scale factor setting in Unity of 0.01 would require this to be set to 100. A scale factor setting of 1, is just 1 here.
var matrix = Matrix4x4.TRS (lightPos, Quaternion.identity, new Vector3 (range*PointLightMeshScaleFactor, range*PointLightMeshScaleFactor, range*PointLightMeshScaleFactor));
Texture cookie = light.light.cookie;
if (cookie != null)
cmd.EnableShaderKeyword ("POINT_COOKIE");
else
cmd.EnableShaderKeyword ("POINT");
if (renderAsQuad)
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DirectionalDeferredLightingMaterial, 0, 0, properties);
else if (intersectsNear)
cmd.DrawMesh (m_PointLightMesh, matrix, m_FiniteNearDeferredLightingMaterial, 0, 0, properties);
else
cmd.DrawMesh (m_PointLightMesh, matrix, m_FiniteDeferredLightingMaterial, 0, 0, properties);
}
Matrix4x4 DirectionalLightmatrix(VisibleLight light, Matrix4x4 worldToLight)
{
// Setup Light Matrix
float scale = 1.0f / light.light.cookieSize;
Matrix4x4 temp1 = Matrix4x4.Scale(new Vector3 (scale, scale, 0.0f));
Matrix4x4 temp2 = Matrix4x4.Translate( new Vector3 (.5f, .5f, 0.0f));
return temp2 * temp1 * worldToLight;
}
void RenderDirectionalLight(VisibleLight light, CommandBuffer cmd, MaterialPropertyBlock properties)
{
var lightToWorld = light.localToWorld;
var worldToLight = lightToWorld.inverse;
// Setup Light Matrix
properties.SetMatrix ("_LightMatrix0", DirectionalLightmatrix (light, worldToLight));
Texture cookie = light.light.cookie;
if (cookie != null) {
cmd.EnableShaderKeyword ("DIRECTIONAL_COOKIE");
} else
cmd.EnableShaderKeyword ("DIRECTIONAL");
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DirectionalDeferredLightingMaterial, 0, 0, properties);
}
void RenderLightsDeferred (CullResults inputs, CommandBuffer cmd)
{
int lightCount = inputs.visibleLights.Count;
for (int lightNum = 0; lightNum < lightCount; lightNum++)
{
VisibleLight light = inputs.visibleLights[lightNum];
bool intersectsNear = (light.flags & VisibleLightFlags.IntersectsNearPlane) != 0;
bool intersectsFar = (light.flags & VisibleLightFlags.IntersectsFarPlane) != 0;
bool renderAsQuad = (intersectsNear && intersectsFar) || (light.lightType == LightType.Directional);
Vector3 lightPos = light.localToWorld.GetColumn (3); //position
Vector3 lightDir = light.localToWorld.GetColumn (2); //z axis
float range = light.range;
var lightToWorld = light.localToWorld;
cmd.SetGlobalMatrix ("unity_WorldToLight", lightToWorld.inverse);
var props = new MaterialPropertyBlock ();
props.SetFloat ("_LightAsQuad", renderAsQuad ? 1 : 0);
props.SetVector ("_LightPos", new Vector4(lightPos.x, lightPos.y, lightPos.z, 1.0f / (range * range)));
props.SetVector ("_LightDir", new Vector4(lightDir.x, lightDir.y, lightDir.z, 0.0f));
props.SetVector ("_LightColor", light.finalColor);
int shadowIdx;
float lightShadowNDXOrNot = m_ShadowIndices.TryGetValue( lightNum, out shadowIdx ) ? (float) shadowIdx : -1.0f;
props.SetFloat ("_LightIndexForShadowMatrixArray", lightShadowNDXOrNot);
props.SetFloat ("_useLegacyCookies", UseLegacyCookies?1.0f:0.0f);
Texture cookie = light.light.cookie;
if (cookie != null)
cmd.SetGlobalTexture ("_LightTexture0", cookie);
cmd.DisableShaderKeyword ("POINT");
cmd.DisableShaderKeyword ("POINT_COOKIE");
cmd.DisableShaderKeyword ("SPOT");
cmd.DisableShaderKeyword ("DIRECTIONAL");
cmd.DisableShaderKeyword ("DIRECTIONAL_COOKIE");
switch (light.lightType)
{
case LightType.Point:
RenderPointLight (light, cmd, props, renderAsQuad, intersectsNear);
break;
case LightType.Spot:
RenderSpotlight (light, cmd, props, renderAsQuad, intersectsNear);
break;
case LightType.Directional:
RenderDirectionalLight(light, cmd, props);
break;
}
}
}
private void InitializeLightData()
{
for (int i = 0; i < k_MaxLights; i++)
{
m_LightData [i] = new Vector4(0.0f, -1.0f, -1.0f, 0.0f);
m_LightColors[i] = Vector4.zero;
m_LightDirections[i] = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
m_LightPositions[i] = Vector4.zero;
m_LightMatrix[i] = Matrix4x4.identity;
m_WorldToLightMatrix[i] = Matrix4x4.identity;
}
}
private void SetupLightShaderVariables(CullResults cull, Camera camera, CommandBuffer cmd)
{
int totalLightCount = cull.visibleLights.Count;
InitializeLightData();
var w = camera.pixelWidth;
var h = camera.pixelHeight;
var viewToWorld = CameraToWorld (camera);
var worldToView = WorldToCamera(camera);
// camera to screen matrix (and it's inverse)
var proj = CameraProjection(camera);
var temp = new Matrix4x4();
temp.SetRow(0, new Vector4(0.5f * w, 0.0f, 0.0f, 0.5f * w));
temp.SetRow(1, new Vector4(0.0f, 0.5f * h, 0.0f, 0.5f * h));
temp.SetRow(2, new Vector4(0.0f, 0.0f, 0.5f, 0.5f));
temp.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
var projscr = temp * proj;
var invProjscr = projscr.inverse;
for (int i = 0; i < totalLightCount; ++i)
{
VisibleLight light = cull.visibleLights [i];
Vector3 lightPos = light.localToWorld.GetColumn (3); //position
Vector3 lightDir = light.localToWorld.GetColumn (2); //z axis
float range = light.range;
float rangeSq = range * range;
var lightToWorld = light.localToWorld;
var worldToLight = lightToWorld.inverse;
var cookie = light.light.cookie;
m_WorldToLightMatrix[i] = worldToLight;
//postiions and directions
m_LightPositions [i] = new Vector4(lightPos.x, lightPos.y, lightPos.z, 1.0f / rangeSq);
m_LightDirections [i] = new Vector4(lightDir.x, lightDir.y, lightDir.z, 0.0f);
//shadow index
int shadowIdx;
float lightShadowNDXOrNot = m_ShadowIndices.TryGetValue(i, out shadowIdx ) ? (float) shadowIdx : -1.0f;
m_LightData[i].y = lightShadowNDXOrNot;
// color
m_LightColors [i] = light.finalColor;
switch (light.lightType)
{
case LightType.Point:
{
m_LightData[i].x = LightDefinitions.SPHERE_LIGHT;
if (cookie != null)
m_LightData[i].z = m_CubeCookieTexArray.FetchSlice(cookie);
break;
}
case LightType.Spot:
{
m_LightData[i].x = LightDefinitions.SPOT_LIGHT;
float chsa = GetCotanHalfSpotAngle (light.spotAngle);
// Setup Light Matrix
m_LightMatrix[i] = SpotlightMatrix (worldToLight, range, chsa);
// If light has cookie use it, otherwise use default cookie set on asset
if (cookie != null)
m_LightData[i].z = m_CookieTexArray.FetchSlice (cookie);
else
m_LightData [i].z = m_CookieTexArray.FetchSlice (m_DefaultSpotCookie);
break;
}
case LightType.Directional:
{
m_LightData[i].x = LightDefinitions.DIRECTIONAL_LIGHT;
// Setup Light Matrix
m_LightMatrix[i] = DirectionalLightmatrix (light, worldToLight);
if (cookie != null)
m_LightData[i].z = m_CookieTexArray.FetchSlice (cookie);
break;
}
}
}
int probeCount = cull.visibleReflectionProbes.Count;
int finalProbeCount = probeCount;
var lightData = new SFiniteLightData[probeCount];
int idx = 0;
// TODO: (cleanup) unify reflection probe setup with deferred
for (int i = 0; i < probeCount; ++i) {
var rl = cull.visibleReflectionProbes [i];
// always a box for now
var cubemap = rl.texture;
if (cubemap == null) {
finalProbeCount--;
continue;
}
var lgtData = new SFiniteLightData();
lgtData.flags = 0;
var bnds = rl.bounds;
var boxOffset = rl.center; // reflection volume offset relative to cube map capture point
var blendDistance = rl.blendDistance;
var mat = rl.localToWorld;
var boxProj = (rl.boxProjection != 0);
var decodeVals = rl.hdr;
// C is reflection volume center in world space (NOT same as cube map capture point)
var e = bnds.extents;
var C = mat.MultiplyPoint(boxOffset);
var combinedExtent = e + new Vector3(blendDistance, blendDistance, blendDistance);
Vector3 vx = mat.GetColumn(0);
Vector3 vy = mat.GetColumn(1);
Vector3 vz = mat.GetColumn(2);
// transform to camera space (becomes a left hand coordinate frame in Unity since Determinant(worldToView)<0)
vx = worldToView.MultiplyVector(vx);
vy = worldToView.MultiplyVector(vy);
vz = worldToView.MultiplyVector(vz);
var Cw = worldToView.MultiplyPoint(C);
if (boxProj) lgtData.flags |= LightDefinitions.IS_BOX_PROJECTED;
lgtData.lightPos = Cw;
lgtData.lightAxisX = vx;
lgtData.lightAxisY = vy;
lgtData.lightAxisZ = vz;
lgtData.localCubeCapturePoint = -boxOffset;
lgtData.probeBlendDistance = blendDistance;
lgtData.lightIntensity = decodeVals.x;
lgtData.decodeExp = decodeVals.y;
lgtData.sliceIndex = m_CubeReflTexArray.FetchSlice(cubemap);
var delta = combinedExtent - e;
lgtData.boxInnerDist = e;
lgtData.boxInvRange.Set(1.0f / delta.x, 1.0f / delta.y, 1.0f / delta.z);
lgtData.lightType = (uint)LightDefinitions.BOX_LIGHT;
lgtData.lightModel = (uint)LightDefinitions.REFLECTION_LIGHT;
lightData [idx++] = lgtData;
}
s_LightDataBuffer.SetData(lightData);
cmd.SetGlobalMatrix("g_mViewToWorld", viewToWorld);
cmd.SetGlobalMatrix("g_mWorldToView", viewToWorld.inverse);
cmd.SetGlobalMatrix("g_mScrProjection", projscr);
cmd.SetGlobalMatrix("g_mInvScrProjection", invProjscr);
cmd.SetGlobalVectorArray("gPerLightData", m_LightData);
cmd.SetGlobalVectorArray("gLightColor", m_LightColors);
cmd.SetGlobalVectorArray("gLightDirection", m_LightDirections);
cmd.SetGlobalVectorArray("gLightPos", m_LightPositions);
cmd.SetGlobalMatrixArray("gLightMatrix", m_LightMatrix);
cmd.SetGlobalMatrixArray("gWorldToLightMatrix", m_WorldToLightMatrix);
cmd.SetGlobalVector("gLightData", new Vector4(totalLightCount, finalProbeCount, 0, 0));
cmd.SetGlobalTexture("_spotCookieTextures", m_CookieTexArray.GetTexCache());
cmd.SetGlobalTexture("_pointCookieTextures", m_CubeCookieTexArray.GetTexCache());
cmd.SetGlobalTexture("_reflCubeTextures", m_CubeReflTexArray.GetTexCache());
cmd.SetGlobalBuffer("g_vProbeData", s_LightDataBuffer);
var topCube = ReflectionProbe.defaultTexture;
var defdecode = ReflectionProbe.defaultTextureHDRDecodeValues;
cmd.SetGlobalTexture("_reflRootCubeTexture", topCube);
cmd.SetGlobalFloat("_reflRootHdrDecodeMult", defdecode.x);
cmd.SetGlobalFloat("_reflRootHdrDecodeExp", defdecode.y);
cmd.SetGlobalFloat ("_useLegacyCookies", UseLegacyCookies?1.0f:0.0f);
cmd.SetGlobalFloat ("_transparencyShadows", TransparencyShadows ? 1.0f : 0.0f);
}
}
}