public SsfPass( RenderPassEvent renderPassEvent, Material material, int blurryIterations, LayerMask layerMask, RenderQueueRange renderQueueRange) { this.renderPassEvent = renderPassEvent; this.material = material; filteringSettings = new FilteringSettings(renderQueueRange, layerMask); blurringTargetHandles = new RenderTargetHandle[blurryIterations]; for (var i = 0; i < blurryIterations; i++) { blurringTargetHandles[i].Init($"_BlurTemp{i}"); } depthTargetHandle.Init("_SsfDepthTexture"); depthNormalTargetHandle.Init("_SsfNormalTexture"); downSamplingPass = material.FindPass("DownSampling"); upSamplingPass = material.FindPass("UpSampling"); depthNormalPass = material.FindPass("DepthNormal"); litPass = material.FindPass("SsfLit"); }
// Here you can implement the rendering logic. // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get(k_RenderTag); using (new ProfilingScope(cmd, new ProfilingSampler("DepthOnlyRenderPass"))) { context.ExecuteCommandBuffer(cmd); cmd.Clear(); var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags; var drawSettings = CreateDrawingSettings(shaderTagId, ref renderingData, sortFlags); drawSettings.perObjectData = PerObjectData.None; RenderQueueRange range = new RenderQueueRange(); range.lowerBound = passSetting.queueMin; range.upperBound = passSetting.queueMax; FilteringSettings filteringSettings = new FilteringSettings(); filteringSettings.renderQueueRange = range; filteringSettings.renderingLayerMask = 1; filteringSettings.layerMask = passSetting.layer; context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filteringSettings); cmd.SetGlobalTexture(depthTexHandle.id, depthTexHandle.Identifier()); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); }
public RenderLayerObjectPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderLayerObjectFeature.RenderQueueType renderQueueType, uint layerMask, RenderLayerObjectFeature.CustomCameraSettings cameraSettings) { m_ProfilerTag = profilerTag; m_ProfilingSampler = new ProfilingSampler(profilerTag); this.renderPassEvent = renderPassEvent; this.renderQueueType = renderQueueType; this.overrideMaterial = null; this.overrideMaterialPassIndex = 0; RenderQueueRange renderQueueRange = (renderQueueType == RenderLayerObjectFeature.RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; m_FilteringSettings = new FilteringSettings(renderQueueRange, -1, layerMask); if (shaderTags != null && shaderTags.Length > 0) { foreach (var passName in shaderTags) { m_ShaderTagIdList.Add(new ShaderTagId(passName)); } } else { m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward")); m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); } m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_CameraSettings = cameraSettings; }
public RenderOpaqueDiscardAndBlendPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { m_ShaderTagIdList.Add(new ShaderTagId("LightweightDiscard")); m_ShaderTagIdList.Add(new ShaderTagId("LightweightBlend")); renderPassEvent = evt; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); }
public DepthNormalsPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); renderPassEvent = evt; depthNormalsMaterial = CoreUtils.CreateEngineMaterial("Hidden/Internal-DepthNormalsTexture"); }
public RenderOpaqueForwardPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); renderPassEvent = evt; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); }
/// <summary> /// Creates a new <c>DepthOnlyPass</c> instance. /// </summary> /// <param name="evt">The <c>RenderPassEvent</c> to use.</param> /// <param name="renderQueueRange">The <c>RenderQueueRange</c> to use for creating filtering settings that control what objects get rendered.</param> /// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param> /// <seealso cref="RenderPassEvent"/> /// <seealso cref="RenderQueueRange"/> /// <seealso cref="LayerMask"/> public DepthOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { base.profilingSampler = new ProfilingSampler(nameof(DepthOnlyPass)); m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); renderPassEvent = evt; useNativeRenderPass = false; }
public GBufferPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference, DeferredLights deferredLights) { base.profilingSampler = new ProfilingSampler(nameof(GBufferPass)); base.renderPassEvent = evt; m_DeferredLights = deferredLights; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_RenderStateBlock.stencilState = stencilState; m_RenderStateBlock.stencilReference = stencilReference; m_RenderStateBlock.mask = RenderStateMask.Stencil; m_ShaderTagValues = new ShaderTagId[4]; m_ShaderTagValues[0] = s_ShaderTagLit; m_ShaderTagValues[1] = s_ShaderTagSimpleLit; m_ShaderTagValues[2] = s_ShaderTagUnlit; m_ShaderTagValues[3] = new ShaderTagId(); // Special catch all case for materials where UniversalMaterialType is not defined or the tag value doesn't match anything we know. m_RenderStateBlocks = new RenderStateBlock[4]; m_RenderStateBlocks[0] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialLit); m_RenderStateBlocks[1] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialSimpleLit); m_RenderStateBlocks[2] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialUnlit); m_RenderStateBlocks[3] = m_RenderStateBlocks[0]; }
public Render2DObjectPass(Renderer2DData rendererData, RenderPassEvent renderPassEvent, string[] lightModeTags, RenderQueueType renderQueueType, int layerMask, RenderObjects.CustomCameraSettings cameraSettings) { renderFeaturePassTag = "RenderFeature " + lightModeTags[0]; //Draw setting 相关流程 this.renderPassEvent = renderPassEvent; this.renderQueueType = renderQueueType; RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); if (s_SortingLayers == null) { s_SortingLayers = SortingLayer.layers; } this.m_CameraSettings = cameraSettings; //Tag for (int i = 0; i < lightModeTags.Length; i++) { var tag = lightModeTags[i]; ShaderTagId sid = new ShaderTagId(tag); k_ShaderTags.Add(sid); } // m_Renderer2DData = rendererData; }
// Tell if we need to show a warning for rendering opaque object and we're in deferred. bool ShowOpaqueObjectWarning() { if (HDRenderPipeline.currentAsset == null) { return(false); } // Only opaque objects are concerned RenderQueueRange currentRange = CustomPassUtils.GetRenderQueueRangeFromRenderQueueType((CustomPass.RenderQueueType)m_RenderQueue.intValue); var allOpaque = HDRenderQueue.k_RenderQueue_AllOpaque; bool customPassQueueContainsOpaqueObjects = currentRange.upperBound >= allOpaque.lowerBound && currentRange.lowerBound <= allOpaque.upperBound; if (!customPassQueueContainsOpaqueObjects) { return(false); } // Only Deferred rendering if (HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.supportedLitShaderMode != RenderPipelineSettings.SupportedLitShaderMode.DeferredOnly) { return(false); } return(true); }
public RenderObjectsToTexturePass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask, RenderObjectsToTexture.CustomCameraSettings cameraSettings) { m_ProfilerTag = profilerTag; this.renderPassEvent = renderPassEvent; this.renderQueueType = renderQueueType; this.overrideMaterial = null; this.overrideMaterialPassIndex = 0; RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); if (shaderTags != null && shaderTags.Length > 0) { foreach (var passName in shaderTags) { m_ShaderTagIdList.Add(new ShaderTagId(passName)); } } else { m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); } m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_CameraSettings = cameraSettings; // Copy depth buffer material m_CopyDepthMaterial = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/Lightweight Render Pipeline/CopyDepth")); }
// Constructor // Copies over settings and sets up member variables public RenderObjectsWithKeywordsPass(string profilerTag, RenderObjectsWithKeywords.Settings settings) { mProfilerTag = profilerTag; mProfilingSampler = new ProfilingSampler(profilerTag); mSettings = settings; RenderQueueRange renderQueueRange = (settings.filter.renderQueueType == RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; mFilteringSettings = new FilteringSettings(renderQueueRange, settings.filter.layerMask); mShaderTagIdList.Add(new ShaderTagId("UniversalForward")); mShaderTagIdList.Add(new ShaderTagId("LightweightForward")); mShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); mRenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); if (settings.dstType == RenderTarget.RenderTextureObject) { mDestination = settings.dstTextureObject; } else { mDestination = new RenderTargetIdentifier(); } mRenderStateBlock.mask |= RenderStateMask.Depth; mRenderStateBlock.depthState = new DepthState(true, CompareFunction.Less); }
public RenderObjectsPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask, RenderObjects.CustomCameraSettings cameraSettings) { m_ProfilerTag = profilerTag; this.renderPassEvent = renderPassEvent; this.renderQueueType = renderQueueType; this.overrideMaterial = null; this.overrideMaterialPassIndex = 0; RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); if (shaderTags != null && shaderTags.Length > 0) { foreach (var passName in shaderTags) { m_ShaderTagIdList.Add(new ShaderTagId(passName)); } } else { m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); } m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_CameraSettings = cameraSettings; _dest = new RenderTargetHandle(); _dest.Init("go_ReflectionTex"); //_dest.id = 1198; }
public RenderMetaballsDepthPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask) { profilingSampler = new ProfilingSampler(nameof(RenderObjectsPass)); _profilingSampler = new ProfilingSampler(profilerTag); this.renderPassEvent = renderPassEvent; this._renderQueueType = renderQueueType; RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; _filteringSettings = new FilteringSettings(renderQueueRange, layerMask); if (shaderTags != null && shaderTags.Length > 0) { foreach (var passName in shaderTags) { _shaderTagIdList.Add(new ShaderTagId(passName)); } } else { _shaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); _shaderTagIdList.Add(new ShaderTagId("UniversalForward")); _shaderTagIdList.Add(new ShaderTagId("UniversalForwardOnly")); _shaderTagIdList.Add(new ShaderTagId("LightweightForward")); } _renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); }
public NormalOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange) { m_FilteringSettings = new FilteringSettings(renderQueueRange); renderPassEvent = evt; normalsMaterial = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/Roystan/Normals Texture")); }
public DrawObjectsRenderPass(RenderPassEvent passEvent, bool opaque, RenderQueueRange range, SortingCriteria criteria) { renderPassEvent = passEvent; isOpaque = opaque; sortingCriteria = criteria; filteringSettings = new FilteringSettings(range); shaderTagId = ShaderTags.ForwardBase; }
public DrawHairShadowMaskPass(string profilerTag, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { m_ProfilerTag = profilerTag; m_ProfilingSampler = new ProfilingSampler(profilerTag); m_ShaderTagIdList.Add(maskTag); renderPassEvent = evt; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); }
public COpapePass(string profilerTag, bool IsOpaque, CRenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { m_ProfilerTag = profilerTag; renderPassEvent = evt; m_ShaderTagIdList.Add(new ShaderTagId("CForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); m_FilterSetting = new FilteringSettings(renderQueueRange, layerMask); m_IsOpaque = IsOpaque; }
/// <summary> /// Create the DepthOnlyPass /// </summary> public DepthOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask) { m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); renderPassEvent = evt; m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag); //test m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward")); m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); }
public CustomRenderPass(Setting setting) { this.setting = setting; RenderQueueRange queue = new RenderQueueRange(); queue.lowerBound = Mathf.Min(setting.queueMax, setting.queueMin); queue.upperBound = Mathf.Max(setting.queueMax, setting.queueMin); filtering = new FilteringSettings(queue, setting.hairLayer); filtering2 = new FilteringSettings(queue, setting.faceLayer); }
public DrawSoildColorPass(setting setting, OutlineRenderFeature render) { mysetting = setting; SelectOutline = render; //过滤设定 RenderQueueRange queue = new RenderQueueRange(); queue.lowerBound = Mathf.Min(setting.QueueMax, setting.QueueMin); queue.upperBound = Mathf.Max(setting.QueueMax, setting.QueueMin); filter = new FilteringSettings(queue, setting.layer); }
public OverdrawPass(string profilerTag, RenderQueueRange renderQueueRange, Shader shader, bool isOpaque) { this.profilerTag = profilerTag; this.isOpaque = isOpaque; profilingSampler = new ProfilingSampler(profilerTag); tagIdList.Add(new ShaderTagId("UniversalForward")); tagIdList.Add(new ShaderTagId("LightweightForward")); tagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); filteringSettings = new FilteringSettings(renderQueueRange, LayerMask.NameToLayer("Everything")); material = CoreUtils.CreateEngineMaterial(shader); }
public static bool GetViewDepthMinMaxWithRenderQueue(Camera camera, RenderQueueRange range, out Vector2 minMax) { minMax = Vector2.zero; bool b = false; Bounds bounds = new Bounds(); Renderer[] coms = Renderer.FindObjectsOfType <Renderer>(); if (null == coms || 0 == coms.Length) { return(false); } foreach (var p in coms) { Renderer r = p.GetComponent <Renderer>(); if (null != r && r.enabled && r.sharedMaterial.renderQueue >= range.min && r.sharedMaterial.renderQueue <= range.max) { if (r is SkinnedMeshRenderer) { (r as SkinnedMeshRenderer).sharedMesh.RecalculateBounds(); } Bounds rb = r.bounds; if (b) { bounds.Encapsulate(rb); } else { bounds = rb; b = true; } } } if (!b) { return(false); } Vector3 fwd = camera.transform.forward; Vector3 c2b = bounds.center - camera.transform.position; float c2bDis = Vector3.Dot(fwd, c2b); float bs = bounds.extents.magnitude; minMax.x = Mathf.Max(0, c2bDis - bs); minMax.y = c2bDis + bs; return(true); }
/// <summary> /// Create the CopyColorPass /// </summary> public OutlinePass(RenderPassEvent renderPassEvent, Material blitMaterial, int blitShaderPassIndex, string tag, int layerMask) { this.renderPassEvent = renderPassEvent; this.blitMaterial = blitMaterial; this.blitShaderPassIndex = blitShaderPassIndex; m_ProfilerTag = tag; m_TemporaryOutlineTexture.Init("_TemporaryOutlineTexture"); RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent) ? RenderQueueRange.transparent : RenderQueueRange.opaque; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); }
public GBufferPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference, DeferredLights deferredLights) { base.renderPassEvent = evt; m_DeferredLights = deferredLights; m_HasDepthPrepass = false; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); if (stencilState.enabled) { m_RenderStateBlock.stencilReference = stencilReference; m_RenderStateBlock.mask = RenderStateMask.Stencil; m_RenderStateBlock.stencilState = stencilState; } }
public DrawObjectsPass(string profilerTag, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) { m_ProfilerTag = profilerTag; m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); renderPassEvent = evt; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); if (stencilState.enabled) { m_RenderStateBlock.stencilReference = stencilReference; m_RenderStateBlock.mask = RenderStateMask.Stencil; m_RenderStateBlock.stencilState = stencilState; } }
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get("RenderMask"); RenderQueueRange renderQueueRange = RenderQueueRange.all; FilteringSettings filters = new FilteringSettings(renderQueueRange); filters.layerMask = LayerMask.GetMask(new string[] { "ViewMasks" }); List <ShaderTagId> m_ShaderTagIdList = new List <ShaderTagId>(); m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); ShaderTagId id = new ShaderTagId("Opaque"); DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, SortingCriteria.CommonOpaque); context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filters); }
private void RenderRendererList(CullResults cullResults, Camera cam, ScriptableRenderContext renderContext, CommandBuffer cmd, ShaderPassName[] passNames, RenderQueueRange inRenderQueueRange, RendererConfiguration rendererConfig = 0, RenderStateBlock?stateBlock = null, Material overrideMaterial = null) { //if (!m_CurrentDebugDisplaySettings.renderingDebugSettings.displayOpaqueObjects) // return; // This is done here because DrawRenderers API lives outside command buffers so we need to make call this before doing any DrawRenders renderContext.ExecuteCommandBuffer(cmd); cmd.Clear(); var drawSettings = new DrawRendererSettings(cam, ClusterShaderPassNames.s_EmptyName) { rendererConfiguration = rendererConfig, sorting = { flags = SortFlags.CommonOpaque } }; for (int i = 0; i < passNames.Length; ++i) { drawSettings.SetShaderPassName(i, passNames[i]); } if (overrideMaterial != null) { drawSettings.SetOverrideMaterial(overrideMaterial, 0); } var filterSettings = new FilterRenderersSettings(true) { renderQueueRange = inRenderQueueRange }; if (stateBlock == null) { renderContext.DrawRenderers(cullResults.visibleRenderers, ref drawSettings, filterSettings); } else { renderContext.DrawRenderers(cullResults.visibleRenderers, ref drawSettings, filterSettings, stateBlock.Value); } }
public DrawOutlinePass(string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) { m_ProfilerTag = profilerTag; m_ProfilingSampler = new ProfilingSampler(profilerTag); m_ShaderTagIdList.Add(new ShaderTagId("Outline")); renderPassEvent = evt; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_IsOpaque = opaque; if (stencilState.enabled) { m_RenderStateBlock.stencilReference = stencilReference; m_RenderStateBlock.mask = RenderStateMask.Stencil; m_RenderStateBlock.stencilState = stencilState; } }
public CustomUberPostPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, Material blitMaterial, CustomUberPostFeature.CustomUberPostSettings settings) { this.renderPassEvent = evt; this.blitMaterial = blitMaterial; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); RefreshSettings(settings); /******** DualKawase ********/ m_Pyramid = new BlurSampleLevel[k_MaxPyramidSize]; for (int i = 0; i < k_MaxPyramidSize; i++) { m_Pyramid[i] = new BlurSampleLevel() { down = Shader.PropertyToID("_BlurMipDown" + i), up = Shader.PropertyToID("_BlurMipUp" + i) }; } }