From 9efb99dbca9904476754cbe7ccfc07ed71062740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Wed, 5 Apr 2023 18:36:04 +0200 Subject: [PATCH] anisotropic map, aniso rotation, import/export GLTF KHR_materials_anisotropy --- .../ScriptingAPI-Documentation.md | 24 ++-- Editor/MaterialWindow.cpp | 57 ++++++--- Editor/MaterialWindow.h | 2 + Editor/ModelImporter_GLTF.cpp | 86 ++++++++++++- WickedEngine/shaders/ShaderInterop_Renderer.h | 48 +++++--- WickedEngine/shaders/brdf.hlsli | 17 +-- WickedEngine/shaders/meshlet_prepareCS.hlsl | 1 + WickedEngine/shaders/objectHF.hlsli | 16 ++- WickedEngine/shaders/surfaceHF.hlsli | 113 ++++++++++++++---- WickedEngine/wiScene.h | 2 +- WickedEngine/wiScene_BindLua.cpp | 1 + WickedEngine/wiScene_Components.cpp | 12 ++ WickedEngine/wiScene_Components.h | 3 + WickedEngine/wiScene_Serializers.cpp | 20 ++++ WickedEngine/wiVersion.cpp | 2 +- features.txt | 1 + 16 files changed, 330 insertions(+), 75 deletions(-) diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md index 4f03fe690..cfb9be605 100644 --- a/Content/Documentation/ScriptingAPI-Documentation.md +++ b/Content/Documentation/ScriptingAPI-Documentation.md @@ -788,18 +788,18 @@ Describes an orientation in 3D space. ```lua TextureSlot = { BASECOLORMAP = 0, - NORMALMAP = 0, - SURFACEMAP = 0, - EMISSIVEMAP = 0, - DISPLACEMENTMAP = 0, - OCCLUSIONMAP = 0, - TRANSMISSIONMAP = 0, - SHEENCOLORMAP = 0, - SHEENROUGHNESSMAP = 0, - CLEARCOATMAP = 0, - CLEARCOATROUGHNESSMAP = 0, - CLEARCOATNORMALMAP = 0, - SPECULARMAP = 0, + NORMALMAP = 1, + SURFACEMAP = 2, + EMISSIVEMAP = 3, + DISPLACEMENTMAP = 4, + OCCLUSIONMAP = 5, + TRANSMISSIONMAP = 6, + SHEENCOLORMAP = 7, + SHEENROUGHNESSMAP = 8, + CLEARCOATMAP = 9, + CLEARCOATROUGHNESSMAP = 10, + CLEARCOATNORMALMAP = 11, + SPECULARMAP = 12, } ``` diff --git a/Editor/MaterialWindow.cpp b/Editor/MaterialWindow.cpp index abe25a3e4..94c7448f0 100644 --- a/Editor/MaterialWindow.cpp +++ b/Editor/MaterialWindow.cpp @@ -9,7 +9,7 @@ void MaterialWindow::Create(EditorComponent* _editor) { editor = _editor; wi::gui::Window::Create(ICON_MATERIAL " Material", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); - SetSize(XMFLOAT2(300, 1300)); + SetSize(XMFLOAT2(300, 1340)); closeButton.SetTooltip("Delete MaterialComponent"); OnClose([=](wi::gui::EventArgs args) { @@ -291,6 +291,7 @@ void MaterialWindow::Create(EditorComponent* _editor) AddWidget(&refractionSlider); pomSlider.Create(0, 1.0f, 0.0f, 1000, "Par Occl Mapping: "); + pomSlider.SetTooltip("[Parallax Occlusion Mapping] Adjust how much the bump map should modulate the surface parallax effect. \nOnly works with PBR + Parallax shader."); pomSlider.SetSize(XMFLOAT2(wid, hei)); pomSlider.SetPos(XMFLOAT2(x, y += step)); pomSlider.OnSlide([&](wi::gui::EventArgs args) { @@ -300,6 +301,29 @@ void MaterialWindow::Create(EditorComponent* _editor) }); AddWidget(&pomSlider); + anisotropyStrengthSlider.Create(0, 1.0f, 0.0f, 1000, "Anisotropy Strength: "); + anisotropyStrengthSlider.SetTooltip("Adjust anisotropy specular effect's strength. \nOnly works with PBR + Anisotropic shader."); + anisotropyStrengthSlider.SetSize(XMFLOAT2(wid, hei)); + anisotropyStrengthSlider.SetPos(XMFLOAT2(x, y += step)); + anisotropyStrengthSlider.OnSlide([&](wi::gui::EventArgs args) { + MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity); + if (material != nullptr) + material->anisotropy_strength = args.fValue; + }); + AddWidget(&anisotropyStrengthSlider); + + anisotropyRotationSlider.Create(0, 360, 0.0f, 360, "Anisotropy Rot: "); + anisotropyRotationSlider.SetTooltip("Adjust anisotropy specular effect's rotation. \nOnly works with PBR + Anisotropic shader."); + anisotropyRotationSlider.SetSize(XMFLOAT2(wid, hei)); + anisotropyRotationSlider.SetPos(XMFLOAT2(x, y += step)); + anisotropyRotationSlider.OnSlide([&](wi::gui::EventArgs args) { + MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity); + if (material != nullptr) + material->anisotropy_rotation = wi::math::DegreesToRadians(args.fValue); + }); + AddWidget(&anisotropyRotationSlider); + + displacementMappingSlider.Create(0, 10.0f, 0.0f, 1000, "Displacement: "); displacementMappingSlider.SetTooltip("Adjust how much the bump map should modulate the geometry when using tessellation."); displacementMappingSlider.SetSize(XMFLOAT2(wid, hei)); @@ -546,6 +570,9 @@ void MaterialWindow::Create(EditorComponent* _editor) case MaterialComponent::SPECULARMAP: textureSlotComboBox.AddItem("Specular map"); break; + case MaterialComponent::ANISOTROPYMAP: + textureSlotComboBox.AddItem("Anisotropy map"); + break; default: break; } @@ -594,6 +621,9 @@ void MaterialWindow::Create(EditorComponent* _editor) case MaterialComponent::SPECULARMAP: textureSlotButton.SetTooltip("RGB: Specular color, A: Specular intensity [non-metal]"); break; + case MaterialComponent::ANISOTROPYMAP: + textureSlotButton.SetTooltip("RG: The anisotropy texture. Red and green channels represent the anisotropy direction in [-1, 1] tangent, bitangent space.\nThe vector is rotated by anisotropyRotation, and multiplied by anisotropyStrength, to obtain the final anisotropy direction and strength."); + break; default: break; } @@ -624,10 +654,16 @@ void MaterialWindow::Create(EditorComponent* _editor) if (material->textures[slot].resource.IsValid()) { + wi::Archive& archive = editor->AdvanceHistory(); + archive << EditorComponent::HISTORYOP_COMPONENT_DATA; + editor->RecordEntity(archive, entity); + material->textures[slot].resource = {}; material->textures[slot].name = ""; material->SetDirty(); textureSlotLabel.SetText(""); + + editor->RecordEntity(archive, entity); } else { @@ -727,6 +763,8 @@ void MaterialWindow::SetEntity(Entity entity) refractionSlider.SetValue(material->refraction); emissiveSlider.SetValue(material->emissiveColor.w); pomSlider.SetValue(material->parallaxOcclusionMapping); + anisotropyStrengthSlider.SetValue(material->anisotropy_strength); + anisotropyRotationSlider.SetValue(wi::math::RadiansToDegrees(material->anisotropy_rotation)); displacementMappingSlider.SetValue(material->displacementMapping); subsurfaceScatteringSlider.SetValue(material->subsurfaceScattering.w); texAnimFrameRateSlider.SetValue(material->texAnimFrameRate); @@ -785,21 +823,6 @@ void MaterialWindow::SetEntity(Entity entity) break; } - switch (material->shaderType) - { - case MaterialComponent::SHADERTYPE_PBR_ANISOTROPIC: - pomSlider.SetText("Anisotropy: "); - pomSlider.SetTooltip("Adjust anisotropy specular effect. \nOnly works with PBR + Anisotropic shader."); - break; - case MaterialComponent::SHADERTYPE_PBR_PARALLAXOCCLUSIONMAPPING: - pomSlider.SetText("Par Occl Mapping: "); - pomSlider.SetTooltip("[Parallax Occlusion Mapping] Adjust how much the bump map should modulate the surface parallax effect. \nOnly works with PBR + Parallax shader."); - break; - default: - pomSlider.SetEnabled(false); - break; - } - sheenRoughnessSlider.SetEnabled(false); clearcoatSlider.SetEnabled(false); clearcoatRoughnessSlider.SetEnabled(false); @@ -909,6 +932,8 @@ void MaterialWindow::ResizeLayout() add(transmissionSlider); add(refractionSlider); add(pomSlider); + add(anisotropyStrengthSlider); + add(anisotropyRotationSlider); add(displacementMappingSlider); add(subsurfaceScatteringSlider); add(texAnimFrameRateSlider); diff --git a/Editor/MaterialWindow.h b/Editor/MaterialWindow.h index 177626964..2d3fc705f 100644 --- a/Editor/MaterialWindow.h +++ b/Editor/MaterialWindow.h @@ -31,6 +31,8 @@ public: wi::gui::Slider transmissionSlider; wi::gui::Slider refractionSlider; wi::gui::Slider pomSlider; + wi::gui::Slider anisotropyStrengthSlider; + wi::gui::Slider anisotropyRotationSlider; wi::gui::Slider displacementMappingSlider; wi::gui::Slider subsurfaceScatteringSlider; wi::gui::Slider texAnimFrameRateSlider; diff --git a/Editor/ModelImporter_GLTF.cpp b/Editor/ModelImporter_GLTF.cpp index 8683e4d03..bd7f590d1 100644 --- a/Editor/ModelImporter_GLTF.cpp +++ b/Editor/ModelImporter_GLTF.cpp @@ -955,6 +955,67 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) } } + auto ext_aniso = x.extensions.find("KHR_materials_anisotropy"); + if (ext_aniso != x.extensions.end()) + { + // https://github.com/ux3d/glTF/tree/extensions/KHR_materials_anisotropy/extensions/2.0/Khronos/KHR_materials_anisotropy + + material.shaderType = MaterialComponent::SHADERTYPE_PBR_ANISOTROPIC; + + if (ext_aniso->second.Has("anisotropyStrength")) + { + auto& factor = ext_aniso->second.Get("anisotropyStrength"); + material.anisotropy_strength = float(factor.IsNumber() ? factor.Get() : factor.Get()); + } + if (ext_aniso->second.Has("anisotropyRotation")) + { + auto& factor = ext_aniso->second.Get("anisotropyRotation"); + material.anisotropy_rotation = float(factor.IsNumber() ? factor.Get() : factor.Get()); + } + if (ext_aniso->second.Has("anisotropyTexture")) + { + auto& param = ext_aniso->second.Get("anisotropyTexture"); + int index = param.Get("index").Get(); + auto& tex = state.gltfModel.textures[index]; + int img_source = tex.source; + if (tex.extensions.count("KHR_texture_basisu")) + { + img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); + } + auto& img = state.gltfModel.images[img_source]; + material.textures[MaterialComponent::ANISOTROPYMAP].resource = wi::resourcemanager::Load(img.uri); + material.textures[MaterialComponent::ANISOTROPYMAP].name = img.uri; + material.textures[MaterialComponent::ANISOTROPYMAP].uvset = (uint32_t)param.Get("texCoord").Get(); + } + + // Differently from the proposed spec, in the proposed sample model, I see different namings: https://github.com/KhronosGroup/glTF-Sample-Models/tree/Anisotropy-Barn-Lamp/2.0/AnisotropyBarnLamp + if (ext_aniso->second.Has("anisotropy")) + { + auto& factor = ext_aniso->second.Get("anisotropy"); + material.anisotropy_strength = float(factor.IsNumber() ? factor.Get() : factor.Get()); + } + if (ext_aniso->second.Has("anisotropyDirection")) + { + auto& factor = ext_aniso->second.Get("anisotropyDirection"); + material.anisotropy_rotation = float(factor.IsNumber() ? factor.Get() : factor.Get()); + } + if (ext_aniso->second.Has("anisotropyDirectionTexture")) + { + auto& param = ext_aniso->second.Get("anisotropyDirectionTexture"); + int index = param.Get("index").Get(); + auto& tex = state.gltfModel.textures[index]; + int img_source = tex.source; + if (tex.extensions.count("KHR_texture_basisu")) + { + img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); + } + auto& img = state.gltfModel.images[img_source]; + material.textures[MaterialComponent::ANISOTROPYMAP].resource = wi::resourcemanager::Load(img.uri); + material.textures[MaterialComponent::ANISOTROPYMAP].name = img.uri; + material.textures[MaterialComponent::ANISOTROPYMAP].uvset = (uint32_t)param.Get("texCoord").Get(); + } + } + } // Create meshes: @@ -3372,6 +3433,7 @@ void ExportModel_GLTF(const std::string& filename, Scene& scene) "KHR_materials_clearcoat", "KHR_materials_ior", "KHR_materials_specular", + "KHR_materials_anisotropy", "KHR_lights_punctual" }; @@ -3656,7 +3718,7 @@ void ExportModel_GLTF(const std::string& filename, Scene& scene) state, wi::helper::GetDirectoryFromPath(filename), material.textures[wi::scene::MaterialComponent::SPECULARMAP].name, - material.textures[wi::scene::MaterialComponent::CLEARCOATMAP].uvset); + material.textures[wi::scene::MaterialComponent::SPECULARMAP].uvset); KHR_materials_specular_builder["specularTexture"] = tinygltf::Value({ {"index",tinygltf::Value(specularTexInfo_pre.index)}, {"texCoord",tinygltf::Value(specularTexInfo_pre.texCoord)} @@ -3668,6 +3730,28 @@ void ExportModel_GLTF(const std::string& filename, Scene& scene) } material_builder.extensions["KHR_materials_specular"] = tinygltf::Value(KHR_materials_specular_builder); + if (material.shaderType == MaterialComponent::SHADERTYPE_PBR_ANISOTROPIC) + { + // Anisotropy Extension (KHR_materials_anisotropy) + tinygltf::Value::Object KHR_materials_anisotropy_builder = { + {"anisotropyStrength", tinygltf::Value(material.anisotropy_strength)}, + {"anisotropyRotation", tinygltf::Value(material.anisotropy_rotation)} + }; + if (material.textures[wi::scene::MaterialComponent::ANISOTROPYMAP].resource.IsValid()) + { + auto specularTexInfo_pre = _ExportHelper_StoreMaterialTexture( + state, + wi::helper::GetDirectoryFromPath(filename), + material.textures[wi::scene::MaterialComponent::ANISOTROPYMAP].name, + material.textures[wi::scene::MaterialComponent::ANISOTROPYMAP].uvset); + KHR_materials_anisotropy_builder["anisotropyTexture"] = tinygltf::Value({ + {"index",tinygltf::Value(specularTexInfo_pre.index)}, + {"texCoord",tinygltf::Value(specularTexInfo_pre.texCoord)} + }); + } + material_builder.extensions["KHR_materials_anisotropy"] = tinygltf::Value(KHR_materials_anisotropy_builder); + } + state.gltfModel.materials.push_back(material_builder); } diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index 1ad432324..cf8467ce2 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -74,21 +74,25 @@ static const uint SHADERMATERIAL_OPTION_BIT_ADDITIVE = 1 << 9; static const uint SHADERMATERIAL_OPTION_BIT_UNLIT = 1 << 10; // Same as MaterialComponent::TEXTURESLOT -// Because of shader compiler issues, the enum is currently not usable in Vulkan for array indexing -static const uint BASECOLORMAP = 0; -static const uint NORMALMAP = 1; -static const uint SURFACEMAP = 2; -static const uint EMISSIVEMAP = 3; -static const uint DISPLACEMENTMAP = 4; -static const uint OCCLUSIONMAP = 5; -static const uint TRANSMISSIONMAP = 6; -static const uint SHEENCOLORMAP = 7; -static const uint SHEENROUGHNESSMAP = 8; -static const uint CLEARCOATMAP = 9; -static const uint CLEARCOATROUGHNESSMAP = 10; -static const uint CLEARCOATNORMALMAP = 11; -static const uint SPECULARMAP = 12; -static const uint TEXTURESLOT_COUNT = 13; +enum TEXTURESLOT +{ + BASECOLORMAP, + NORMALMAP, + SURFACEMAP, + EMISSIVEMAP, + DISPLACEMENTMAP, + OCCLUSIONMAP, + TRANSMISSIONMAP, + SHEENCOLORMAP, + SHEENROUGHNESSMAP, + CLEARCOATMAP, + CLEARCOATROUGHNESSMAP, + CLEARCOATNORMALMAP, + SPECULARMAP, + ANISOTROPYMAP, + + TEXTURESLOT_COUNT +}; static const uint SVT_TILE_SIZE = 256u; static const uint SVT_TILE_BORDER = 4u; @@ -331,6 +335,11 @@ struct ShaderMaterial float clearcoatRoughness; uint stencilRef; + float anisotropy_strength; + float anisotropy_rotation_sin; + float anisotropy_rotation_cos; + float padding0; + int sampler_descriptor; uint options; uint layerMask; @@ -497,6 +506,7 @@ struct ShaderMeshlet uint instanceIndex; uint geometryIndex; uint primitiveOffset; + uint padding; }; struct ShaderTransform @@ -1089,6 +1099,7 @@ struct MipgenPushConstants int texture_input; int texture_output; int sampler_index; + int padding; }; static const uint MIPGEN_OPTION_BIT_PRESERVE_COVERAGE = 1 << 0; static const uint MIPGEN_OPTION_BIT_SRGB = 1 << 1; @@ -1103,6 +1114,8 @@ struct FilterEnvmapPushConstants uint padding_filterCB; int texture_input; int texture_output; + int padding0; + int padding1; }; // CopyTexture2D params: @@ -1161,6 +1174,7 @@ struct SkinningPushConstants uint morphbuffer_offset; uint morph_count; int morphvb_index; + int padding; }; struct MorphTargetGPU @@ -1206,6 +1220,7 @@ struct TerrainVirtualTexturePush uint write_size; float resolution_rcp; int region_weights_textureRO; + int padding; }; struct VirtualTextureResidencyUpdateCB { @@ -1233,6 +1248,9 @@ struct VirtualTextureTileRequestsPush uint height; int feedbackTextureRO; int requestBufferRW; + int padding0; + int padding1; + int padding2; }; diff --git a/WickedEngine/shaders/brdf.hlsli b/WickedEngine/shaders/brdf.hlsli index 2ca800a2c..70d8f5600 100644 --- a/WickedEngine/shaders/brdf.hlsli +++ b/WickedEngine/shaders/brdf.hlsli @@ -102,11 +102,12 @@ struct SurfaceToLight float VdotH; // cos angle between view direction and half vector float3 F; // fresnel term computed from VdotH - // Aniso params: +#ifdef ANISOTROPIC float TdotL; float BdotL; float TdotH; float BdotH; +#endif // ANISOTROPIC inline void create(in Surface surface, in float3 Lnormalized) { @@ -127,10 +128,12 @@ struct SurfaceToLight F = F_Schlick(surface.f0, surface.f90, VdotH); - TdotL = dot(surface.T.xyz, L); - BdotL = dot(surface.B, L); - TdotH = dot(surface.T.xyz, H); - BdotH = dot(surface.B, H); +#ifdef ANISOTROPIC + TdotL = dot(surface.aniso.T.xyz, L); + BdotL = dot(surface.aniso.B, L); + TdotH = dot(surface.aniso.T.xyz, H); + BdotH = dot(surface.aniso.B, H); +#endif // ANISOTROPIC #ifdef CARTOON // SSS is handled differently in cartoon shader: @@ -157,8 +160,8 @@ struct SurfaceToLight float3 BRDF_GetSpecular(in Surface surface, in SurfaceToLight surface_to_light) { #ifdef ANISOTROPIC - float D = D_GGX_Anisotropic(surface.at, surface.ab, surface_to_light.TdotH, surface_to_light.BdotH, surface_to_light.NdotH); - float Vis = V_SmithGGXCorrelated_Anisotropic(surface.at, surface.ab, surface.TdotV, surface.BdotV, + float D = D_GGX_Anisotropic(surface.aniso.at, surface.aniso.ab, surface_to_light.TdotH, surface_to_light.BdotH, surface_to_light.NdotH); + float Vis = V_SmithGGXCorrelated_Anisotropic(surface.aniso.at, surface.aniso.ab, surface.aniso.TdotV, surface.aniso.BdotV, surface_to_light.TdotL, surface_to_light.BdotL, surface.NdotV, surface_to_light.NdotL); #else float D = D_GGX(surface.roughnessBRDF, surface_to_light.NdotH, surface_to_light.H); diff --git a/WickedEngine/shaders/meshlet_prepareCS.hlsl b/WickedEngine/shaders/meshlet_prepareCS.hlsl index 583348cb8..833fa551c 100644 --- a/WickedEngine/shaders/meshlet_prepareCS.hlsl +++ b/WickedEngine/shaders/meshlet_prepareCS.hlsl @@ -20,6 +20,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn meshlet.instanceIndex = instanceIndex; meshlet.geometryIndex = geometryIndex; meshlet.primitiveOffset = j * MESHLET_TRIANGLE_COUNT; + meshlet.padding = 0; uint meshletIndex = inst.meshletOffset + geometry.meshletOffset + j; output_meshlets.Store(meshletIndex * sizeof(ShaderMeshlet), meshlet); diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli index c680f7d4a..13bb915d7 100644 --- a/WickedEngine/shaders/objectHF.hlsli +++ b/WickedEngine/shaders/objectHF.hlsli @@ -1273,7 +1273,21 @@ float4 main(PixelInput input, in bool is_frontface : SV_IsFrontFace) : SV_Target #ifdef ANISOTROPIC - surface.anisotropy = GetMaterial().parallaxOcclusionMapping; + surface.aniso.strength = GetMaterial().anisotropy_strength; + surface.aniso.direction = float2(GetMaterial().anisotropy_rotation_cos, GetMaterial().anisotropy_rotation_sin); + +#ifdef OBJECTSHADER_USE_UVSETS + [branch] + if (GetMaterial().textures[ANISOTROPYMAP].IsValid()) + { + float2 anisotropyTexture = GetMaterial().textures[ANISOTROPYMAP].Sample(sampler_objectshader, input.uvsets).rg * 2 - 1; + surface.aniso.strength *= length(anisotropyTexture); + surface.aniso.direction = mul(float2x2(surface.aniso.direction.x, surface.aniso.direction.y, -surface.aniso.direction.y, surface.aniso.direction.x), normalize(anisotropyTexture)); + } +#endif // OBJECTSHADER_USE_UVSETS + + surface.aniso.T = normalize(mul(TBN, float3(surface.aniso.direction, 0))); + #endif // ANISOTROPIC diff --git a/WickedEngine/shaders/surfaceHF.hlsli b/WickedEngine/shaders/surfaceHF.hlsli index bd8107551..7c07075fd 100644 --- a/WickedEngine/shaders/surfaceHF.hlsli +++ b/WickedEngine/shaders/surfaceHF.hlsli @@ -33,6 +33,20 @@ struct ClearcoatSurface float3 F; }; +struct AnisotropicSurface +{ + float2 direction; + float strength; + float3 T; + + // computed values: + float at; + float ab; + float3 B; + float TdotV; + float BdotV; +}; + enum { SURFACE_FLAG_BACKFACE = 1u << 0u, @@ -60,14 +74,12 @@ struct Surface float2 screenUV; // pixel coordinate in UV space [0 -> 1] (used for randomization effects) float4 T; // tangent float3 B; // bitangent - float anisotropy; // anisotropy factor [0 -> 1] float4 sss; // subsurface scattering color * amount float4 sss_inv; // 1 / (1 + sss) uint layerMask; // the engine-side layer mask float3 facenormal; // surface normal without normal map uint flags; uint uid_validate; - RayCone raycone; float hit_depth; float3 gi; float3 bumpColor; @@ -78,13 +90,22 @@ struct Surface float f90; // reflectance at grazing angle float3 R; // reflection vector float3 F; // fresnel term computed from NdotV - float TdotV; - float BdotV; - float at; - float ab; +#ifdef SURFACE_LOAD_MIPCONE + RayCone raycone; +#endif // SURFACE_LOAD_MIPCONE + +#ifdef SHEEN SheenSurface sheen; +#endif // SHEEN + +#ifdef CLEARCOAT ClearcoatSurface clearcoat; +#endif // CLEARCOAT + +#ifdef ANISOTROPIC + AnisotropicSurface aniso; +#endif // ANISOTROPIC inline void init() { @@ -104,7 +125,6 @@ struct Surface screenUV = 0; T = 0; B = 0; - anisotropy = 0; sss = 0; sss_inv = 1; layerMask = ~0; @@ -113,16 +133,29 @@ struct Surface gi = 0; bumpColor = 0; + uid_validate = 0; + hit_depth = 0; + +#ifdef SURFACE_LOAD_MIPCONE + raycone = (RayCone)0; +#endif // SURFACE_LOAD_MIPCONE + +#ifdef SHEEN sheen.color = 0; sheen.roughness = 0; +#endif // SHEEN +#ifdef CLEARCOAT clearcoat.factor = 0; clearcoat.roughness = 0; clearcoat.N = 0; +#endif // CLEARCOAT - uid_validate = 0; - raycone = (RayCone)0; - hit_depth = 0; +#ifdef ANISOTROPIC + aniso.strength = 0; + aniso.direction = float2(1, 0); + aniso.T = 0; +#endif // ANISOTROPIC } inline void create(in ShaderMaterial material) @@ -134,10 +167,6 @@ struct Surface { flags |= SURFACE_FLAG_RECEIVE_SHADOW; } - -#ifdef ANISOTROPIC - anisotropy = material.parallaxOcclusionMapping; -#endif // ANISOTROPIC } inline void create( @@ -199,31 +228,44 @@ struct Surface roughness = clamp(roughness, 0.045, 1); roughnessBRDF = roughness * roughness; +#ifdef SHEEN sheen.roughness = clamp(sheen.roughness, 0.045, 1); sheen.roughnessBRDF = sheen.roughness * sheen.roughness; +#endif // SHEEN +#ifdef CLEARCOAT clearcoat.roughness = clamp(clearcoat.roughness, 0.045, 1); clearcoat.roughnessBRDF = clearcoat.roughness * clearcoat.roughness; +#endif // CLEARCOAT NdotV = saturate(dot(N, V) + 1e-5); f90 = saturate(50.0 * dot(f0, 0.33)); F = F_Schlick(f0, f90, NdotV); - clearcoat.F = F_Schlick(f0, f90, saturate(dot(clearcoat.N, V) + 1e-5)); - clearcoat.F *= clearcoat.factor; R = -reflect(V, N); - clearcoat.R = -reflect(V, clearcoat.N); +#ifdef SHEEN // Sheen energy compensation: https://dassaultsystemes-technology.github.io/EnterprisePBRShadingModel/spec-2021x.md.html#figure_energy-compensation-sheen-e sheen.DFG = texture_sheenlut.SampleLevel(sampler_linear_clamp, float2(NdotV, sheen.roughness), 0).r; sheen.albedoScaling = 1.0 - max3(sheen.color) * sheen.DFG; +#endif // SHEEN + +#ifdef CLEARCOAT + clearcoat.F = F_Schlick(f0, f90, saturate(dot(clearcoat.N, V) + 1e-5)); + clearcoat.F *= clearcoat.factor; + clearcoat.R = -reflect(V, clearcoat.N); +#endif // CLEARCOAT B = normalize(cross(T.xyz, N) * T.w); // Compute bitangent again after normal mapping - TdotV = dot(T.xyz, V); - BdotV = dot(B, V); - at = max(0, roughnessBRDF * (1 + anisotropy)); - ab = max(0, roughnessBRDF * (1 - anisotropy)); + +#ifdef ANISOTROPIC + aniso.B = normalize(cross(N, aniso.T)); + aniso.TdotV = dot(aniso.T.xyz, V); + aniso.BdotV = dot(aniso.B, V); + aniso.at = max(0, roughnessBRDF * (1 + aniso.strength)); + aniso.ab = max(0, roughnessBRDF * (1 - aniso.strength)); +#endif // ANISOTROPIC #ifdef CARTOON F = smoothstep(0.1, 0.5, F); @@ -393,6 +435,33 @@ struct Surface bumpColor = bumpColor * 2 - 1; bumpColor.rg *= material.normalMapStrength; } + + +#ifdef ANISOTROPIC + aniso.strength = material.anisotropy_strength; + aniso.direction = float2(material.anisotropy_rotation_cos, material.anisotropy_rotation_sin); + + [branch] + if (material.textures[ANISOTROPYMAP].IsValid()) + { +#ifdef SURFACE_LOAD_QUAD_DERIVATIVES + float2 anisotropyTexture = material.textures[ANISOTROPYMAP].SampleGrad(sam, uvsets, uvsets_dx, uvsets_dy).rg * 2 - 1; +#else + float lod = 0; +#ifdef SURFACE_LOAD_MIPCONE + lod = compute_texture_lod(material.textures[ANISOTROPYMAP].GetTexture(), material.textures[ANISOTROPYMAP].GetUVSet() == 0 ? lod_constant0 : lod_constant1, ray_direction, surf_normal, cone_width); +#endif // SURFACE_LOAD_MIPCONE + float2 anisotropyTexture = material.textures[ANISOTROPYMAP].SampleLevel(sam, uvsets, lod).rg * 2 - 1; +#endif // SURFACE_LOAD_QUAD_DERIVATIVES + + aniso.strength *= length(anisotropyTexture); + aniso.direction = mul(float2x2(aniso.direction.x, aniso.direction.y, -aniso.direction.y, aniso.direction.x), normalize(anisotropyTexture)); + } + + aniso.T = normalize(mul(TBN, float3(aniso.direction, 0))); + +#endif // ANISOTROPIC + } baseColor = is_emittedparticle ? 1 : material.baseColor; @@ -663,6 +732,8 @@ struct Surface #endif // SURFACE_LOAD_QUAD_DERIVATIVES } + + #ifdef SHEEN sheen.color = material.GetSheenColor(); sheen.roughness = material.sheenRoughness; diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 8c9357dda..99f232ff1 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -29,7 +29,7 @@ namespace wi::scene wi::ecs::ComponentManager& layers = componentLibrary.Register("wi::scene::Scene::layers"); wi::ecs::ComponentManager& transforms = componentLibrary.Register("wi::scene::Scene::transforms"); wi::ecs::ComponentManager& hierarchy = componentLibrary.Register("wi::scene::Scene::hierarchy"); - wi::ecs::ComponentManager& materials = componentLibrary.Register("wi::scene::Scene::materials", 1); // version = 1 + wi::ecs::ComponentManager& materials = componentLibrary.Register("wi::scene::Scene::materials", 2); // version = 2 wi::ecs::ComponentManager& meshes = componentLibrary.Register("wi::scene::Scene::meshes", 2); // version = 2 wi::ecs::ComponentManager& impostors = componentLibrary.Register("wi::scene::Scene::impostors"); wi::ecs::ComponentManager& objects = componentLibrary.Register("wi::scene::Scene::objects", 2); // version = 2 diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp index 613dc1b05..3585ccd97 100644 --- a/WickedEngine/wiScene_BindLua.cpp +++ b/WickedEngine/wiScene_BindLua.cpp @@ -337,6 +337,7 @@ TextureSlot = { CLEARCOATROUGHNESSMAP = 10, CLEARCOATNORMALMAP = 11, SPECULARMAP = 12, + ANISOTROPYMAP = 13, } ExpressionPreset = { diff --git a/WickedEngine/wiScene_Components.cpp b/WickedEngine/wiScene_Components.cpp index 9ddaea055..afe6d3d07 100644 --- a/WickedEngine/wiScene_Components.cpp +++ b/WickedEngine/wiScene_Components.cpp @@ -260,6 +260,18 @@ namespace wi::scene material.alphaTest = 1 - alphaRef; material.layerMask = layerMask; material.transmission = transmission; + if (shaderType == SHADERTYPE_PBR_ANISOTROPIC) + { + material.anisotropy_strength = wi::math::Clamp(anisotropy_strength, 0, 0.99f); + material.anisotropy_rotation_sin = std::sin(anisotropy_rotation); + material.anisotropy_rotation_cos = std::cos(anisotropy_rotation); + } + else + { + material.anisotropy_strength = 0; + material.anisotropy_rotation_sin = 0; + material.anisotropy_rotation_cos = 0; + } material.stencilRef = wi::renderer::CombineStencilrefs(engineStencilRef, userStencilRef); material.shaderType = (uint)shaderType; material.userdata = userdata; diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index aa9a0b63b..2300ad802 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -170,6 +170,8 @@ namespace wi::scene float refraction = 0.0f; float transmission = 0.0f; float alphaRef = 1.0f; + float anisotropy_strength = 0; + float anisotropy_rotation = 0; //radians, counter-clockwise XMFLOAT4 sheenColor = XMFLOAT4(1, 1, 1, 1); float sheenRoughness = 0; @@ -197,6 +199,7 @@ namespace wi::scene CLEARCOATROUGHNESSMAP, CLEARCOATNORMALMAP, SPECULARMAP, + ANISOTROPYMAP, TEXTURESLOT_COUNT }; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 43b673da4..7e723f9f1 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -219,6 +219,18 @@ namespace wi::scene archive >> userdata; } + if (seri.GetVersion() >= 2) + { + archive >> anisotropy_strength; + archive >> anisotropy_rotation; + archive >> textures[ANISOTROPYMAP].name; + archive >> textures[ANISOTROPYMAP].uvset; + } + else + { + anisotropy_strength = parallaxOcclusionMapping; // old version fix + } + for (auto& x : textures) { if (!x.name.empty()) @@ -349,6 +361,14 @@ namespace wi::scene { archive << userdata; } + + if (seri.GetVersion() >= 2) + { + archive << anisotropy_strength; + archive << anisotropy_rotation; + archive << textures[ANISOTROPYMAP].name; + archive << textures[ANISOTROPYMAP].uvset; + } } } void MeshComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index c7b359598..21358ac50 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 190; + const int revision = 191; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision); diff --git a/features.txt b/features.txt index bbd418ef2..65133383a 100644 --- a/features.txt +++ b/features.txt @@ -94,6 +94,7 @@ KHR_materials_pbrSpecularGlossiness KHR_materials_sheen KHR_materials_clearcoat KHR_materials_specular +KHR_materials_anisotropy KHR_materials_ior KHR_texture_basisu KHR_lights_punctual