diff --git a/Content/Editor/MaterialTemplates/GUI.shader b/Content/Editor/MaterialTemplates/GUI.shader index c15b7f20f..0fd68d4c9 100644 --- a/Content/Editor/MaterialTemplates/GUI.shader +++ b/Content/Editor/MaterialTemplates/GUI.shader @@ -2,6 +2,7 @@ // Version: @0 #define MATERIAL 1 +#define MaterialTextureMipBias 0.0f @3 #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" diff --git a/Content/Editor/MaterialTemplates/PostProcess.shader b/Content/Editor/MaterialTemplates/PostProcess.shader index 1ef747ecb..6b63ef2e9 100644 --- a/Content/Editor/MaterialTemplates/PostProcess.shader +++ b/Content/Editor/MaterialTemplates/PostProcess.shader @@ -2,6 +2,7 @@ // Version: @0 #define MATERIAL 1 +#define MaterialTextureMipBias 0.0f @3 #include "./Flax/Common.hlsl" diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index 929cb153b..7ce269fb7 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -316,7 +316,7 @@ namespace FlaxEditor.Surface.Archetypes DefaultValues = new object[] { (int)CommonSamplerType.LinearClamp, // Sampler - -1.0f, // Level + -1.0f, // Level (-1 when unused) 0, // Texture Group }, Elements = new[] @@ -476,7 +476,7 @@ namespace FlaxEditor.Surface.Archetypes DefaultValues = new object[] { (int)CommonSamplerType.LinearWrap, // Sampler - -1.0f, // Level + -1.0f, // Level (-1 when unused) 0, // Texture Group }, Elements = new[] diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index 51c6d49fd..c1faa53da 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -37,7 +37,8 @@ GPU_CB_STRUCT(MaterialShaderDataPerView { Float4 TemporalAAJitter; Float3 LargeWorldsChunkIndex; float LargeWorldsChunkSize; - Float3 ViewPadding0; + float MaterialTextureMipBias; + float ViewPadding0; float TestValue; float ScaledTimeParam; }); @@ -87,6 +88,7 @@ void IMaterial::BindParameters::BindViewData() cb.TemporalAAJitter = view.TemporalAAJitter; cb.LargeWorldsChunkIndex = LargeWorlds::Enable ? (Float3)Int3(view.Origin / LargeWorlds::ChunkSize) : Float3::Zero; cb.LargeWorldsChunkSize = LargeWorlds::ChunkSize; + cb.MaterialTextureMipBias = RenderContext.List->Setup.MaterialTextureMipBias; cb.TestValue = Graphics::TestValue; // Update constants diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index c14c54851..fa3d47629 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 185 +#define MATERIAL_GRAPH_VERSION 186 class Material; class GPUShader; diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 73daf937e..b350419e7 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -286,6 +286,11 @@ public: /// API_FIELD() float RenderScale = 1.0f; + /// + /// The texture mip level bias for materials. Positive values make textures appear blurrier; negative values sharpen. + /// + API_FIELD() float MaterialTextureMipBias = 0.0f; + /// /// The image resolution upscale location within rendering pipeline. Unused if RenderingPercentage is 1. /// diff --git a/Source/Engine/Renderer/RenderSetup.h b/Source/Engine/Renderer/RenderSetup.h index bba7738b5..b22b00cc8 100644 --- a/Source/Engine/Renderer/RenderSetup.h +++ b/Source/Engine/Renderer/RenderSetup.h @@ -11,6 +11,7 @@ struct FLAXENGINE_API RenderSetup { RenderingUpscaleLocation UpscaleLocation = RenderingUpscaleLocation::DuringAntiAliasing; float RenderScale = 1.0f; + float MaterialTextureMipBias = 0.0f; bool UseShadows = false; bool UseMotionVectors = false; bool UseTemporalAAJitter = false; diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 7b3f64727..e8f3bcb6a 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -426,6 +426,11 @@ PRAGMA_ENABLE_DEPRECATION_WARNINGS setup.UseGlobalSurfaceAtlas; setup.UseVolumetricFog = (renderContext.View.Flags & ViewFlags::Fog) != ViewFlags::None; + // Calculate material texture mip bias + setup.MaterialTextureMipBias = renderContext.Task->MaterialTextureMipBias; + if (setup.RenderScale < 1.0f) // Increase textures sharpness when upscaling (eg. -1.0 bias when using 2x upscaling) + setup.MaterialTextureMipBias += Math::Log2(setup.RenderScale); + // Disable TAA jitter in debug modes switch (renderContext.View.Mode) { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Parameters.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Parameters.cpp index 8def3ce62..d24cf5117 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Parameters.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Parameters.cpp @@ -71,11 +71,13 @@ void MaterialGenerator::ProcessGroupParameters(Box* box, Node* node, Value& valu case MaterialParameterType::CubeTexture: case MaterialParameterType::NormalMap: case MaterialParameterType::Texture: + sampleTexture(node, value, box, param, true); + break; case MaterialParameterType::GPUTextureArray: case MaterialParameterType::GPUTextureCube: case MaterialParameterType::GPUTextureVolume: case MaterialParameterType::GPUTexture: - sampleTexture(node, value, box, param); + sampleTexture(node, value, box, param, false); break; default: CRASH; break; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index e09b1e9ad..ee47db85e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -24,7 +24,7 @@ namespace }; }; -MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture) +MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture, bool useGlobalMipBias) { ASSERT(texture && box); const auto parent = box->GetParent>(); @@ -60,15 +60,27 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B // Check if hasn't been sampled during that tree eating if (valueBox->Cache.IsInvalid()) { - bool canUseSample = CanUseSample(_treeType); + bool useLevel = !CanUseSample(_treeType), useBias = false; String mipLevel = TEXT("0"); const auto layer = GetRootLayer(); if (layer && layer->Domain == MaterialDomain::Decal && _treeType == MaterialTreeType::PixelShader) { // Decals use computed mip level due to ddx/ddy being unreliable - canUseSample = false; + useLevel = true; mipLevel = String::Format(TEXT("CalculateTextureMipmap(input, {})"), texture->ShaderName); } + if (useGlobalMipBias) + { + if (useLevel) + { + mipLevel += TEXT(" + MaterialTextureMipBias"); + } + else + { + useBias = true; + mipLevel = TEXT("MaterialTextureMipBias"); + } + } // Check if use custom UVs String uv; @@ -93,12 +105,8 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B uv = use3dUVs ? TEXT("float3(input.TexCoord.xy, 0)") : TEXT("input.TexCoord.xy"); } - // Select sampler - // TODO: add option for texture groups and per texture options like wrap mode etc. - // TODO: changing texture sampler option - const Char* sampler = TEXT("SamplerLinearWrap"); - // Sample texture + const Char* sampler = TEXT("SamplerLinearWrap"); if (texture->AsInteger == (int32)MaterialSceneTextures::SceneDepth) { // Sample depth buffer @@ -107,9 +115,8 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B } else if (isNormalMap) { - const Char* format = canUseSample ? TEXT("{0}.Sample({1}, {2}).xyz") : TEXT("{0}.SampleLevel({1}, {2}, {3}).xyz"); - // Sample encoded normal map + const Char* format = useLevel ? TEXT("{0}.SampleLevel({1}, {2}, {3}).xyz") : (useBias ? TEXT("{0}.SampleBias({1}, {2}, {3}).xyz") : TEXT("{0}.Sample({1}, {2}).xyz")); const String sampledValue = String::Format(format, texture->ShaderName, sampler, uv, mipLevel); const auto normalVector = writeLocal(VariantType::Float3, sampledValue, parent); @@ -120,27 +127,8 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B } else { - // Select format string based on texture type - const Char* format; - /*if (isCubemap) - { - MISSING_CODE("sampling cube maps and 3d texture in material generator"); - //format = TEXT("SAMPLE_CUBEMAP({0}, {1})"); - } - else*/ - { - /*if (useCustomUVs) - { - createGradients(writer, parent); - format = TEXT("SAMPLE_TEXTURE_GRAD({0}, {1}, {2}, {3})"); - } - else*/ - { - format = canUseSample ? TEXT("{0}.Sample({1}, {2})") : TEXT("{0}.SampleLevel({1}, {2}, {3})"); - } - } - // Sample texture + const Char* format = useLevel ? TEXT("{0}.SampleLevel({1}, {2}, {3})") : (useBias ? TEXT("{0}.SampleBias({1}, {2}, {3})") : TEXT("{0}.Sample({1}, {2})")); String sampledValue = String::Format(format, texture->ShaderName, sampler, uv, mipLevel); valueBox->Cache = writeLocal(VariantType::Float4, sampledValue, parent); } @@ -149,9 +137,9 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B return &valueBox->Cache; } -void MaterialGenerator::sampleTexture(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture) +void MaterialGenerator::sampleTexture(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture, bool useGlobalMipBias) { - const auto sample = sampleTextureRaw(caller, value, box, texture); + const auto sample = sampleTextureRaw(caller, value, box, texture, useGlobalMipBias); if (sample == nullptr) return; @@ -202,16 +190,26 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) { // Texture case 1: + // Cube Texture + case 3: + // Normal Map + case 4: { // Check if texture has been selected Guid textureId = (Guid)node->Values[0]; if (textureId.IsValid()) { // Get or create parameter for that texture - auto param = findOrAddTexture(textureId); + SerializedMaterialParam param; + if (node->TypeID == 1) // Texture + param = findOrAddTexture(textureId); + else if (node->TypeID == 3) // Cube Texture + param = findOrAddCubeTexture(textureId); + else if (node->TypeID == 4) // Normal Map + param = findOrAddNormalMap(textureId); // Sample texture - sampleTexture(node, value, box, ¶m); + sampleTexture(node, value, box, ¶m, true); } else { @@ -236,46 +234,6 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) } break; } - // Cube Texture - case 3: - { - // Check if texture has been selected - Guid textureId = (Guid)node->Values[0]; - if (textureId.IsValid()) - { - // Get or create parameter for that cube texture - auto param = findOrAddCubeTexture(textureId); - - // Sample texture - sampleTexture(node, value, box, ¶m); - } - else - { - // Use default value - value = Value::Zero; - } - break; - } - // Normal Map - case 4: - { - // Check if texture has been selected - Guid textureId = (Guid)node->Values[0]; - if (textureId.IsValid()) - { - // Get or create parameter for that texture - auto param = findOrAddNormalMap(textureId); - - // Sample texture - sampleTexture(node, value, box, ¶m); - } - else - { - // Use default value - value = Value::Zero; - } - break; - } // Parallax Occlusion Mapping case 5: { @@ -327,10 +285,11 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) " float currRayHeight = 1.0;\n" " float lastSampledHeight = 1;\n" " int currSample = 0;\n" + " float mipBiasDerivScale = pow(2, MaterialTextureMipBias);\n" " while (currSample < (int)numSamples)\n" " {{\n" - " float currSampledHeight = {1}.SampleGrad(SamplerLinearWrap, {10} + currOffset, {5}, {6}){7};\n" + " float currSampledHeight = {1}.SampleGrad(SamplerLinearWrap, {10} + currOffset, {5} * mipBiasDerivScale, {6} * mipBiasDerivScale){7};\n" " if (currSampledHeight > currRayHeight)\n" " {{\n" @@ -584,6 +543,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) const bool isVolume = textureParam->Type == MaterialParameterType::GPUTextureVolume; const bool isNormalMap = textureParam->Type == MaterialParameterType::NormalMap; const bool use3dUVs = isCubemap || isArray || isVolume; + const bool useGlobalMipBias = true; // Could be exposed as node option (separate check or within new set of CommonSamplerType values) uvs = Value::Cast(uvs, use3dUVs ? VariantType::Float3 : VariantType::Float2); // Get other inputs @@ -613,7 +573,16 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) { // Sample Texture const Char* format; - if (useLevel || !canUseSample) + if (useGlobalMipBias) + { + if (useLevel && useOffset) + format = TEXT("{0}.SampleLevel({1}, {2}, {3} + MaterialTextureMipBias, {4})"); + else if (useOffset) + format = TEXT("{0}.SampleBias({1}, {2}, MaterialTextureMipBias, {4})"); + else + format = TEXT("{0}.SampleBias({1}, {2}, MaterialTextureMipBias)"); + } + else if (useLevel || !canUseSample) { if (useOffset) format = TEXT("{0}.SampleLevel({1}, {2}, {3}, {4})"); @@ -662,8 +631,9 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) " uv1 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex1)) * 43758.5453);\n" " uv2 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex2)) * 43758.5453);\n" " uv3 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex3)) * 43758.5453);\n" - " float2 fdx = ddx({0});\n" - " float2 fdy = ddy({0});\n" + " float mipBiasDerivScale = pow(2, MaterialTextureMipBias);\n" + " float2 fdx = ddx({0}) * mipBiasDerivScale;\n" + " float2 fdy = ddy({0}) * mipBiasDerivScale;\n" " float4 tex1 = {1}.SampleGrad({2}, uv1, fdx, fdy, {4}) * weights.x;\n" " float4 tex2 = {1}.SampleGrad({2}, uv2, fdx, fdy, {4}) * weights.y;\n" " float4 tex3 = {1}.SampleGrad({2}, uv3, fdx, fdy, {4}) * weights.z;\n" diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h index d80e1e359..9446924aa 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h @@ -179,8 +179,8 @@ private: SerializedMaterialParam* findParam(const Guid& id, MaterialLayer* layer); MaterialGraphParameter* findGraphParam(const Guid& id); - MaterialValue* sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture); - void sampleTexture(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture); + MaterialValue* sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture, bool useGlobalMipBias = false); + void sampleTexture(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture, bool useGlobalMipBias = false); void sampleSceneDepth(Node* caller, Value& value, Box* box); void linearizeSceneDepth(Node* caller, const Value& depth, Value& value); diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 09530eb16..ab44e5081 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -181,7 +181,8 @@ cbuffer ViewData : register(b1) float4 TemporalAAJitter; float3 LargeWorldsChunkIndex; float LargeWorldsChunkSize; - float2 ViewPadding0; + float MaterialTextureMipBias; + float ViewPadding0; float TestValue; float ScaledTimeParam; };