From 01e87c6bd47ff1c0b226b659240bbab3808643b5 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 30 Aug 2023 20:49:51 -0700 Subject: [PATCH 1/8] feat: allow user specific configs closes #66 --- src/State.cpp | 15 ++++++++++----- src/State.h | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/State.cpp b/src/State.cpp index f9305081a..ebad28c86 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -53,13 +53,18 @@ void State::Load() { auto& shaderCache = SIE::ShaderCache::Instance(); - std::string configPath = "Data\\SKSE\\Plugins\\CommunityShaders.json"; - + std::string configPath = userConfigPath; std::ifstream i(configPath); if (!i.is_open()) { - logger::error("Error opening config file ({})\n", configPath); - return; + logger::info("Unable to open user config file ({}); trying default ({})", configPath, defaultConfigPath); + configPath = defaultConfigPath; + i.open(configPath); + if (!i.is_open()) { + logger::error("Error opening config file ({})\n", configPath); + return; + } } + logger::info("Loading config file ({})", configPath); json settings; try { @@ -115,7 +120,7 @@ void State::Load() void State::Save() { auto& shaderCache = SIE::ShaderCache::Instance(); - std::ofstream o(L"Data\\SKSE\\Plugins\\CommunityShaders.json"); + std::ofstream o(userConfigPath); json settings; Menu::GetSingleton()->Save(settings); diff --git a/src/State.h b/src/State.h index eee740dd8..d47c7f7ae 100644 --- a/src/State.h +++ b/src/State.h @@ -20,6 +20,8 @@ class State spdlog::level::level_enum logLevel = spdlog::level::info; std::string shaderDefinesString = ""; std::vector> shaderDefines{}; // data structure to parse string into; needed to avoid dangling pointers + const std::string userConfigPath = "Data\\SKSE\\Plugins\\CommunityShadersUSER.json"; + const std::string defaultConfigPath = "Data\\SKSE\\Plugins\\CommunityShaders.json"; void Draw(); void Reset(); From 36931b1b110cec52a7639c244f8d17b17abbe4ee Mon Sep 17 00:00:00 2001 From: alandtse Date: Thu, 31 Aug 2023 03:53:29 +0000 Subject: [PATCH 2/8] =?UTF-8?q?style:=20=F0=9F=8E=A8=20apply=20clang-forma?= =?UTF-8?q?t=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/Grass Lighting/Shaders/RunGrass.hlsl | 8 ++--- .../Shaders/LightLimitFix/LightLimitFix.hlsli | 31 +++++++++---------- src/Features/LightLimitFix.cpp | 12 +++---- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/features/Grass Lighting/Shaders/RunGrass.hlsl b/features/Grass Lighting/Shaders/RunGrass.hlsl index 9440473b3..6c8572297 100644 --- a/features/Grass Lighting/Shaders/RunGrass.hlsl +++ b/features/Grass Lighting/Shaders/RunGrass.hlsl @@ -532,18 +532,18 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float intensityMultiplier = 1 - intensityFactor * intensityFactor; float3 lightColor = light.color.xyz; - float3 normalizedLightDirection = normalize(lightDirection); + float3 normalizedLightDirection = normalize(lightDirection); float lightAngle = dot(worldNormal.xyz, normalizedLightDirection.xyz); - - if (light.shadowMode){ + + if (light.shadowMode) { float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection, true, eyeIndex); if (light.shadowMode == 2) lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, 0.0, eyeIndex); else lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex); } - + float3 lightDiffuseColor = lightColor * saturate(lightAngle.xxx); lightDiffuseColor += subsurfaceColor * lightColor * GetSoftLightMultiplier(lightAngle, SubsurfaceScatteringAmount); diff --git a/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli b/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli index f11512c13..e88e79e04 100644 --- a/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli +++ b/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli @@ -122,8 +122,6 @@ float ContactShadows(float3 rayPos, float2 texcoord, float offset, float3 lightD # define DRAW_IN_WORLDSPACE #endif - - // Copyright 2019 Google LLC. // SPDX-License-Identifier: Apache-2.0 @@ -138,19 +136,18 @@ float ContactShadows(float3 rayPos, float2 texcoord, float offset, float3 lightD float3 TurboColormap(float x) { - const float4 kRedVec4 = float4(0.13572138, 4.61539260, -42.66032258, 132.13108234); - const float4 kGreenVec4 = float4(0.09140261, 2.19418839, 4.84296658, -14.18503333); - const float4 kBlueVec4 = float4(0.10667330, 12.64194608, -60.58204836, 110.36276771); - const float2 kRedVec2 = float2(-152.94239396, 59.28637943); - const float2 kGreenVec2 = float2(4.27729857, 2.82956604); - const float2 kBlueVec2 = float2(-89.90310912, 27.34824973); - - x = saturate(x); - float4 v4 = float4(1.0, x, x * x, x * x * x); - float2 v2 = v4.zw * v4.z; - return float3( - dot(v4, kRedVec4) + dot(v2, kRedVec2), - dot(v4, kGreenVec4) + dot(v2, kGreenVec2), - dot(v4, kBlueVec4) + dot(v2, kBlueVec2) - ); + const float4 kRedVec4 = float4(0.13572138, 4.61539260, -42.66032258, 132.13108234); + const float4 kGreenVec4 = float4(0.09140261, 2.19418839, 4.84296658, -14.18503333); + const float4 kBlueVec4 = float4(0.10667330, 12.64194608, -60.58204836, 110.36276771); + const float2 kRedVec2 = float2(-152.94239396, 59.28637943); + const float2 kGreenVec2 = float2(4.27729857, 2.82956604); + const float2 kBlueVec2 = float2(-89.90310912, 27.34824973); + + x = saturate(x); + float4 v4 = float4(1.0, x, x * x, x * x * x); + float2 v2 = v4.zw * v4.z; + return float3( + dot(v4, kRedVec4) + dot(v2, kRedVec2), + dot(v4, kGreenVec4) + dot(v2, kGreenVec2), + dot(v4, kBlueVec4) + dot(v2, kBlueVec2)); } \ No newline at end of file diff --git a/src/Features/LightLimitFix.cpp b/src/Features/LightLimitFix.cpp index fb44000f7..ca18b28b5 100644 --- a/src/Features/LightLimitFix.cpp +++ b/src/Features/LightLimitFix.cpp @@ -200,10 +200,10 @@ void LightLimitFix::SetLightPosition(LightLimitFix::LightData& a_light, RE::NiPo for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) { auto eyePosition = eyeCount == 1 ? state->GetRuntimeData().posAdjust.getEye(eyeIndex) : - state->GetVRRuntimeData().posAdjust.getEye(eyeIndex); + state->GetVRRuntimeData().posAdjust.getEye(eyeIndex); auto viewMatrix = eyeCount == 1 ? state->GetRuntimeData().cameraData.getEye(eyeIndex).viewMat : - state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; + state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; auto worldPos = a_initialPosition - eyePosition; a_light.positionWS[eyeIndex].x = worldPos.x; a_light.positionWS[eyeIndex].y = worldPos.y; @@ -316,7 +316,7 @@ void LightLimitFix::Bind() auto reflections = (!REL::Module::IsVR() ? RE::BSGraphics::RendererShadowState::GetSingleton()->GetRuntimeData().cubeMapRenderTarget : - RE::BSGraphics::RendererShadowState::GetSingleton()->GetVRRuntimeData().cubeMapRenderTarget) == RE::RENDER_TARGETS_CUBEMAP::kREFLECTIONS; + RE::BSGraphics::RendererShadowState::GetSingleton()->GetVRRuntimeData().cubeMapRenderTarget) == RE::RENDER_TARGETS_CUBEMAP::kREFLECTIONS; if (reflections || accumulator->GetRuntimeData().activeShadowSceneNode != RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]) { PerPass perPassData{}; @@ -629,12 +629,12 @@ bool LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData auto state = RE::BSGraphics::RendererShadowState::GetSingleton(); auto eyePosition = eyeCount == 1 ? state->GetRuntimeData().posAdjust.getEye(a_eyeIndex) : - state->GetVRRuntimeData().posAdjust.getEye(a_eyeIndex); + state->GetVRRuntimeData().posAdjust.getEye(a_eyeIndex); cachedParticleLight.position = { light.positionWS[a_eyeIndex].x + eyePosition.x, light.positionWS[a_eyeIndex].y + eyePosition.y, light.positionWS[a_eyeIndex].z + eyePosition.z }; for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) { auto viewMatrix = eyeCount == 1 ? state->GetRuntimeData().cameraData.getEye(eyeIndex).viewMat : - state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; + state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; light.positionVS[eyeIndex] = DirectX::SimpleMath::Vector3::Transform(light.positionWS[eyeIndex], viewMatrix); } for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) { @@ -758,7 +758,7 @@ void LightLimitFix::UpdateLights() auto eyeIndex = 0; // only calculate for left eye auto eyePosition = eyeCount == 1 ? state->GetRuntimeData().posAdjust.getEye(eyeIndex) : - state->GetVRRuntimeData().posAdjust.getEye(eyeIndex); + state->GetVRRuntimeData().posAdjust.getEye(eyeIndex); for (const auto& particleLight : particleLights) { if (const auto particleSystem = netimmerse_cast(particleLight.first); From bede9eb5371bbbbb59d0aefbe5fb4a74ce836b8f Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 30 Aug 2023 23:55:03 -0700 Subject: [PATCH 3/8] feat: upgrade older config files --- src/State.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/State.cpp b/src/State.cpp index ebad28c86..bf44dc97e 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -115,6 +115,11 @@ void State::Load() for (auto* feature : Feature::GetFeatureList()) feature->Load(settings); + i.close(); + if (settings["Version"].is_string() && settings["Version"].get() != Plugin::VERSION.string()) { + logger::info("Found older config for version {}; upgrading to {}", (std::string) settings["Version"], Plugin::VERSION.string()); + Save(); + } } void State::Save() From 174f2c62e8a29a1f2c806e953302fc9b4b23bbb8 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 30 Aug 2023 23:55:19 -0700 Subject: [PATCH 4/8] chore: add save debug statement --- src/State.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/State.cpp b/src/State.cpp index bf44dc97e..966d6d38a 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -155,6 +155,7 @@ void State::Save() feature->Save(settings); o << settings.dump(1); + logger::info("Saving settings to {}", userConfigPath); } bool State::ValidateCache(CSimpleIniA& a_ini) From b732b2ec533bf215a12cbc60d5b77f538db5ef49 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 30 Aug 2023 23:56:11 -0700 Subject: [PATCH 5/8] fix: fix reset of log on settings load The early part of the log was being destroyed after loading the debug level. --- src/XSEPlugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index f19794bc0..b5922c5ad 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -152,7 +152,8 @@ bool Load() auto state = State::GetSingleton(); state->Load(); - InitializeLog(state->GetLogLevel()); + auto log = spdlog::default_logger(); + log->set_level(state->GetLogLevel()); return true; } \ No newline at end of file From aff7d7f2b846fe04f12ca0e97b4947c1e018e55a Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 30 Aug 2023 23:56:34 -0700 Subject: [PATCH 6/8] chore: add successful load debug statement --- src/XSEPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index b5922c5ad..ed321fa24 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -48,7 +48,7 @@ extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* a_s while (!WinAPI::IsDebuggerPresent()) {}; #endif InitializeLog(); - logger::info("Loaded plugin"); + logger::info("Loaded {} {}", Plugin::NAME, Plugin::VERSION.string()); SKSE::Init(a_skse); return Load(); } From 44705840cf27d83ae1e219cfb4ad59681ad4a48d Mon Sep 17 00:00:00 2001 From: alandtse Date: Thu, 31 Aug 2023 06:57:22 +0000 Subject: [PATCH 7/8] =?UTF-8?q?style:=20=F0=9F=8E=A8=20apply=20clang-forma?= =?UTF-8?q?t=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/State.cpp b/src/State.cpp index 966d6d38a..0b2d36ead 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -117,7 +117,7 @@ void State::Load() feature->Load(settings); i.close(); if (settings["Version"].is_string() && settings["Version"].get() != Plugin::VERSION.string()) { - logger::info("Found older config for version {}; upgrading to {}", (std::string) settings["Version"], Plugin::VERSION.string()); + logger::info("Found older config for version {}; upgrading to {}", (std::string)settings["Version"], Plugin::VERSION.string()); Save(); } } From 97747fc68c56fe57fa78d718f663b746f245b70f Mon Sep 17 00:00:00 2001 From: alandtse Date: Thu, 31 Aug 2023 20:04:59 +0000 Subject: [PATCH 8/8] =?UTF-8?q?style:=20=F0=9F=8E=A8=20apply=20clang-forma?= =?UTF-8?q?t=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Shaders/LightLimitFix/LightLimitFix.hlsli | 3 +- package/Shaders/Lighting.hlsl | 55 +++++++++---------- src/Features/LightLimitFix.cpp | 5 +- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli b/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli index 90fee7f0d..d8d1c088d 100644 --- a/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli +++ b/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli @@ -45,7 +45,8 @@ uint GetClusterIndex(float2 uv, float z) uint clusterZ = uint(max((log2(clampedZ) - log2(perPassLLF[0].LightsNear)) * 16.0 / log2(perPassLLF[0].LightsFar / perPassLLF[0].LightsNear), 0.0)); uint2 clusterDim = ceil(perPassLLF[0].BufferDim / float2(32, 16)); uint3 cluster = uint3(uint2((uv * perPassLLF[0].BufferDim) / clusterDim), clusterZ); - return cluster.x + (32 * cluster.y) + (32 * 16 * cluster.z);; + return cluster.x + (32 * cluster.y) + (32 * 16 * cluster.z); + ; } // Get a raw depth from the depth buffer. [0,1] in uv space diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 7ba716efb..f469c5364 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -1685,7 +1685,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float screenNoise = InterleavedGradientNoise(screenUV * perPassLLF[0].BufferDim); # if defined(SKINNED) || !defined(MODELSPACENORMALS) float3 vertexNormal = normalize(transpose(tbn)[2]); -# endif +# endif # endif if (numLights > 0) { @@ -1713,28 +1713,28 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace lightColor *= shadowComponent.xxx; # endif float3 normalizedLightDirection = normalize(lightDirection); -# if defined(LIGHT_LIMIT_FIX) -# if defined(DEFSHADOW) +# if defined(LIGHT_LIMIT_FIX) +# if defined(DEFSHADOW) if (perPassLLF[0].EnableContactShadows && !FrameParams.z && FrameParams.y && shadowComponent != 0.0) { -# else +# else if (perPassLLF[0].EnableContactShadows && !FrameParams.z && FrameParams.y) { -# endif +# endif float3 normalizedLightDirectionWS = normalizedLightDirection; -# if (defined(SKINNED) || !defined(MODELSPACENORMALS)) && !defined(DRAW_IN_WORLDSPACE) +# if (defined(SKINNED) || !defined(MODELSPACENORMALS)) && !defined(DRAW_IN_WORLDSPACE) if (!input.WorldSpace) { normalizedLightDirectionWS = normalize(mul(input.World[eyeIndex], float4(normalizedLightDirection, 0))); - } -# endif + } +# endif float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirectionWS, true, eyeIndex); - -# if defined(SKINNED) || !defined(MODELSPACENORMALS) + +# if defined(SKINNED) || !defined(MODELSPACENORMALS) float shadowIntensityFactor = saturate(dot(vertexNormal, normalizedLightDirection.xyz) * PI); lightColor *= lerp(1.0, ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, 1.0, 0.0, eyeIndex), shadowIntensityFactor); -# else +# else lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, 1.0, 0.0, eyeIndex); -# endif +# endif } -# endif +# endif # if defined(CPM_AVAILABLE) if (perPassParallax[0].EnableShadows) { @@ -1754,8 +1754,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (perPassParallax[0].EnableTerrainParallax) lightColor *= GetParallaxSoftShadowMultiplierTerrain(input, terrainUVs, mipLevel, lightDirectionTS, sh0, lightIsLit * parallaxShadowQuality); # elif defined(ENVMAP) - if (complexMaterialParallax) - lightColor *= GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, lightIsLit * parallaxShadowQuality); + if (complexMaterialParallax) + lightColor *= GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, lightIsLit * parallaxShadowQuality); # endif } # endif @@ -1846,15 +1846,15 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (!FrameParams.z && FrameParams.y) { float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection, true, eyeIndex); - if (light.firstPersonShadow){ + if (light.firstPersonShadow) { lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, shadowQualityScale, light.radius, eyeIndex); - } else if (perPassLLF[0].EnableContactShadows){ + } else if (perPassLLF[0].EnableContactShadows) { # if defined(SKINNED) || !defined(MODELSPACENORMALS) float shadowIntensityFactor = saturate(dot(worldSpaceVertexNormal, normalizedLightDirection.xyz) * PI); lightColor *= lerp(1.0, ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, shadowQualityScale, 0.0, eyeIndex), shadowIntensityFactor); # else lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, shadowQualityScale, 0.0, eyeIndex); -# endif +# endif } } @@ -2029,7 +2029,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif // !defined(PBR) - # if defined(LANDSCAPE) && !defined(LOD_LAND_BLEND) psout.Albedo.w = 0; # else @@ -2083,17 +2082,17 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # if defined(LIGHT_LIMIT_FIX) -if (perPassLLF[0].EnableLightsVisualisation){ - if (perPassLLF[0].LightsVisualisationMode == 0){ - psout.Albedo.xyz = TurboColormap(perPassLLF[0].StrictLightsCount > 7); - } else if (perPassLLF[0].LightsVisualisationMode == 1){ - psout.Albedo.xyz = TurboColormap((float)perPassLLF[0].StrictLightsCount / 7.0); + if (perPassLLF[0].EnableLightsVisualisation) { + if (perPassLLF[0].LightsVisualisationMode == 0) { + psout.Albedo.xyz = TurboColormap(perPassLLF[0].StrictLightsCount > 7); + } else if (perPassLLF[0].LightsVisualisationMode == 1) { + psout.Albedo.xyz = TurboColormap((float)perPassLLF[0].StrictLightsCount / 7.0); + } else { + psout.Albedo.xyz = TurboColormap((float)lightCount / 128.0); + } } else { - psout.Albedo.xyz = TurboColormap((float)lightCount / 128.0); + psout.Albedo.xyz = color.xyz - tmpColor.xyz * FrameParams.zzz; } -} else { - psout.Albedo.xyz = color.xyz - tmpColor.xyz * FrameParams.zzz; -} # else psout.Albedo.xyz = color.xyz - tmpColor.xyz * FrameParams.zzz; # endif // defined(LIGHT_LIMIT_FIX) diff --git a/src/Features/LightLimitFix.cpp b/src/Features/LightLimitFix.cpp index d25a7d3b0..e4100a72e 100644 --- a/src/Features/LightLimitFix.cpp +++ b/src/Features/LightLimitFix.cpp @@ -431,14 +431,12 @@ bool LightLimitFix::CheckParticleLights(RE::BSRenderPass* a_pass, uint32_t) } } - if (gradientConfig) { auto grey = float3(config->colorMult.red, config->colorMult.green, config->colorMult.blue).Dot(float3(0.3f, 0.59f, 0.11f)); color.red *= grey * gradientConfig->color.red; color.green *= grey * gradientConfig->color.green; color.blue *= grey * gradientConfig->color.blue; - } - else { + } else { color.red *= config->colorMult.red; color.green *= config->colorMult.green; color.blue *= config->colorMult.blue; @@ -455,7 +453,6 @@ bool LightLimitFix::CheckParticleLights(RE::BSRenderPass* a_pass, uint32_t) return false; } - enum class GrassShaderTechniques { RenderDepth = 8,