From 9b7f7a5d04f902583452a10a3189b87bcb782d49 Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Tue, 18 Feb 2025 22:33:16 +0000 Subject: [PATCH] [forest] added curved normals for the grass blades and fix a crash where brixelizer gi was trying to compute gi for each grass blade --- data/shaders/common_resources.hlsl | 5 +- data/shaders/common_vertex_processing.hlsl | 122 ++++++++++----------- runtime/RHI/Vulkan/Vulkan_FidelityFX.cpp | 10 +- runtime/Rendering/Material.cpp | 4 +- runtime/Rendering/Material.h | 3 +- runtime/Rendering/Renderer.cpp | 5 +- runtime/Rendering/Renderer_Buffers.h | 9 +- runtime/World/Components/Renderable.cpp | 13 ++- 8 files changed, 92 insertions(+), 79 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index c3cd0bd63..8cebe94fc 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -91,12 +91,13 @@ struct MaterialParameters float height; uint flags; - float world_space_height; + float local_width; float ior; float subsurface_scattering; float sheen; - float3 padding; + float local_height; + float2 padding; float anisotropic; float anisotropic_rotation; diff --git a/data/shaders/common_vertex_processing.hlsl b/data/shaders/common_vertex_processing.hlsl index 94c7a1de7..4e375666c 100644 --- a/data/shaders/common_vertex_processing.hlsl +++ b/data/shaders/common_vertex_processing.hlsl @@ -53,29 +53,55 @@ struct gbuffer_vertex matrix transform_previous : TRANSFORM_PREVIOUS; }; +static float hash(float n) +{ + return frac(sin(n) * 43758.5453f); +} + +static float perlin_noise(float x) +{ + float i = floor(x); + float f = frac(x); + f = f * f * (3.0 - 2.0 * f); + + return lerp(hash(i), hash(i + 1.0), f); +} + static float3 extract_position(matrix transform) { return float3(transform._31, transform._32, transform._33); } +static float3 rotate_x(float angle, float3 v) +{ + float c = cos(angle); + float s = sin(angle); + + // standard x-axis rotation + return float3( + v.x, + c * v.y - s * v.z, + s * v.y + c * v.z + ); +} + +static float3 rotate_y(float angle, float3 v) +{ + float c = cos(angle); + float s = sin(angle); + + // apply standard y-axis rotation + return float3( + c * v.x + s * v.z, + v.y, + -s * v.x + c * v.z + ); +} + struct vertex_processing { struct vegetation { - static float hash(float n) - { - return frac(sin(n) * 43758.5453f); - } - - static float perlin_noise(float x) - { - float i = floor(x); - float f = frac(x); - f = f * f * (3.0 - 2.0 * f); - - return lerp(hash(i), hash(i + 1.0), f); - } - static float3 apply_wind(uint instance_id, float3 position_vertex, float height_percent, float3 wind, float time) { const float sway_extent = 0.2f; // maximum sway amplitude @@ -130,7 +156,7 @@ struct vertex_processing return position_vertex; } - static float3 apply_bend_from_gravity(float3 position_vertex, uint instance_id) + static float3 apply_bend_from_gravity(float3 position_vertex, uint instance_id, float height_percent) { // generate a random direction for bending based on instance_id float3 random_direction = float3( @@ -143,42 +169,7 @@ struct vertex_processing random_direction = normalize(random_direction); // apply the bend - float3 height_percent = position_vertex.y / GetMaterial().world_space_height; - position_vertex += random_direction * 0.1 * height_percent; - - return position_vertex; - } - - static float3 generate_curved_grass_blade_normals(float3 normal, float3 position) - { - const float rotationAngle = 0.3; - - // Generate two rotated normals - float c1 = cos(PI * rotationAngle); - float s1 = sin(PI * rotationAngle); - float3 rotatedNormal1 = float3( - c1 * normal.x + s1 * normal.z, - normal.y, - -s1 * normal.x + c1 * normal.z - ); - - float c2 = cos(PI * -rotationAngle); - float s2 = sin(PI * -rotationAngle); - float3 rotatedNormal2 = float3( - c2 * normal.x + s2 * normal.z, - normal.y, - -s2 * normal.x + c2 * normal.z - ); - - // Assuming 'widthPercent' can be calculated or passed as a parameter here - // For demonstration, let's use y position as a proxy for width - float widthPercent = saturate(position.y); // Example: using y position as a proxy for width - - // Mix the rotated normals - float3 curvedNormal = lerp(rotatedNormal1, rotatedNormal2, widthPercent); - - // Normalize the result - return normalize(curvedNormal); + return position_vertex += random_direction * height_percent; } }; @@ -277,31 +268,38 @@ struct vertex_processing gbuffer_vertex transform_to_world_space(Vertex_PosUvNorTan input, uint instance_id, matrix transform) { - gbuffer_vertex vertex; - MaterialParameters material = GetMaterial(); Surface surface; surface.flags = material.flags; - // compute uv + // start building the vertex + gbuffer_vertex vertex; vertex.uv = float2(input.uv.x * material.tiling.x + material.offset.x, input.uv.y * material.tiling.y + material.offset.y); vertex.color = 1.0f; - // compute height percent, this means the height of the vertex in relation to the mesh's height - float3 position_transform = extract_position(transform); // world space - float3 animation_pivot = position_transform; // bottom of the mesh by default - float height_percent = (input.position.xyz.y - animation_pivot.y) / GetMaterial().world_space_height; + // compute width and height percent, they represent the position of the vertex relative to the grass blade + float3 position_transform = extract_position(transform); // bottom-left of the grass blade + float width_percent = (input.position.xyz.x) / GetMaterial().local_width; + float height_percent = (input.position.xyz.y - position_transform.x) / GetMaterial().local_height; - // generate blade normals for grass + // hacky way to detect grass blade and enchance it with certain modifications in local space if (surface.vertex_animate_gravity()) { + // give it a nice color gradient float3 color_base = float3(0.05f, 0.2f, 0.01f); // darker green float3 color_tip = float3(0.5f, 0.5f, 0.1f); // yellowish vertex.color = lerp(color_base, color_tip, smoothstep(0, 1, height_percent * 0.5f)); - - //input.position.xyz = vertex_processing::vegetation::generate_curved_grass_blade_normals(input.normal, input.position.xyz); - //input.position.xyz = vertex_processing::vegetation::apply_bend_from_gravity(input.position.xyz, instance_id); + + // replace flat normals with curved ones + const float rotation = 60.0f * DEG_TO_RAD; + float3 rotated_normal_left = rotate_y(rotation, input.normal); + float3 rotated_normal_right = rotate_y(-rotation, input.normal); + input.normal = normalize(lerp(rotated_normal_left, rotated_normal_right, width_percent)); + + // bend the top due to gravity + //input.position.xyz = vertex_processing::vegetation::apply_bend_from_gravity(input.position.xyz, instance_id, height_percent); } +; // compute the final world transform bool is_instanced = instance_id != 0; // not ideal as you can have instancing with instance_id = 0, however it's very performant branching due to predictability diff --git a/runtime/RHI/Vulkan/Vulkan_FidelityFX.cpp b/runtime/RHI/Vulkan/Vulkan_FidelityFX.cpp index 1d0db8e9c..6e6eec3b4 100644 --- a/runtime/RHI/Vulkan/Vulkan_FidelityFX.cpp +++ b/runtime/RHI/Vulkan/Vulkan_FidelityFX.cpp @@ -30,10 +30,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "../RHI_Device.h" #include "../RHI_SwapChain.h" #include "../RHI_Queue.h" -#include "../RHI_Shader.h" #include "../RHI_Pipeline.h" +#include "../RHI_Shader.h" #include "../Rendering/Renderer_Buffers.h" #include "../Rendering/Renderer.h" +#include "../Rendering/Material.h" #include "../World/Components/Renderable.h" #include "../World/Components/Camera.h" #include "../World/Entity.h" @@ -1187,7 +1188,12 @@ namespace spartan // process entities for (int64_t i = index_start; i < index_end; i++) { - auto& entity = entities[i]; + shared_ptr& entity = entities[i]; + + // skip grass blades, it's going to bring it to a crawl + if (entity->GetComponent()->GetMaterial()->GetProperty(MaterialProperty::AnimationFoliageGravity)) + continue; + uint64_t entity_id = entity->GetObjectId(); brixelizer_gi::entity_map[entity_id] = entity; bool is_dynamic = entity->IsMoving(); diff --git a/runtime/Rendering/Material.cpp b/runtime/Rendering/Material.cpp index 54c23ffa4..fcfa86d21 100644 --- a/runtime/Rendering/Material.cpp +++ b/runtime/Rendering/Material.cpp @@ -46,7 +46,7 @@ namespace spartan switch (material_property) { case MaterialProperty::Optimized: return "optimized"; - case MaterialProperty::WorldSpaceHeight: return "world_space_height"; + case MaterialProperty::LocalHeight: return "world_space_height"; case MaterialProperty::Clearcoat: return "clearcoat"; case MaterialProperty::Clearcoat_Roughness: return "clearcoat_roughness"; case MaterialProperty::Anisotropic: return "anisotropic"; @@ -136,7 +136,7 @@ namespace spartan SetProperty(MaterialProperty::Roughness, 1.0f); SetProperty(MaterialProperty::TextureTilingX, 1.0f); SetProperty(MaterialProperty::TextureTilingY, 1.0f); - SetProperty(MaterialProperty::WorldSpaceHeight, 1.0f); + SetProperty(MaterialProperty::LocalHeight, 1.0f); SetProperty(MaterialProperty::Ior, Material::EnumToIor(MaterialIor::Air)); } diff --git a/runtime/Rendering/Material.h b/runtime/Rendering/Material.h index 29c43f3eb..8e2abe1fe 100644 --- a/runtime/Rendering/Material.h +++ b/runtime/Rendering/Material.h @@ -47,7 +47,8 @@ namespace spartan enum class MaterialProperty { Optimized, // indicates if the material has been optimized (textures packed and compressed) - WorldSpaceHeight, // height of the mesh to which the material is applied + LocalHeight, // height of the mesh to which the material is applied + LocalWidth, // width of the mesh to which the material is applied Clearcoat, // additional specular layer on top of the base specular Clearcoat_Roughness, // roughness level of the clearcoat layer Anisotropic, // controls the anisotropy level of specular reflections diff --git a/runtime/Rendering/Renderer.cpp b/runtime/Rendering/Renderer.cpp index 9ee40cb13..50f380b37 100644 --- a/runtime/Rendering/Renderer.cpp +++ b/runtime/Rendering/Renderer.cpp @@ -921,8 +921,9 @@ namespace spartan // properties { SP_ASSERT(index < rhi_max_array_size); - - properties[index].world_space_height = material->GetProperty(MaterialProperty::WorldSpaceHeight); + + properties[index].local_width = material->GetProperty(MaterialProperty::LocalWidth); + properties[index].local_height = material->GetProperty(MaterialProperty::LocalHeight); properties[index].color.x = material->GetProperty(MaterialProperty::ColorR); properties[index].color.y = material->GetProperty(MaterialProperty::ColorG); properties[index].color.z = material->GetProperty(MaterialProperty::ColorB); diff --git a/runtime/Rendering/Renderer_Buffers.h b/runtime/Rendering/Renderer_Buffers.h index 195de929f..51e609ae5 100644 --- a/runtime/Rendering/Renderer_Buffers.h +++ b/runtime/Rendering/Renderer_Buffers.h @@ -163,13 +163,14 @@ namespace spartan float normal_mul = 0.0f; float height_mul = 0.0f; - uint32_t flags = 0; - float world_space_height = 0.0f; - float ior = 1.0f; + uint32_t flags = 0; + float local_width = 0.0f; + float ior = 1.0f; float subsurface_scattering; float sheen; - math::Vector3 padding; + float local_height = 0.0f; + math::Vector2 padding; float anisotropic; float anisotropic_rotation; diff --git a/runtime/World/Components/Renderable.cpp b/runtime/World/Components/Renderable.cpp index bcaf95696..eb5013cb3 100644 --- a/runtime/World/Components/Renderable.cpp +++ b/runtime/World/Components/Renderable.cpp @@ -239,22 +239,27 @@ namespace spartan m_material->PrepareForGpu(); } - // compute mesh height + // compute local dimensions { vector vertices; GetGeometry(nullptr, &vertices); - + float min_height = FLT_MAX; float max_height = -FLT_MAX; + float min_width = FLT_MAX; + float max_width = -FLT_MAX; Matrix transform = HasInstancing() ? GetEntity()->GetMatrix() * GetInstanceTransform(0) : GetEntity()->GetMatrix(); for (const RHI_Vertex_PosTexNorTan& vertex : vertices) { Vector3 position = Vector3(vertex.pos[0], vertex.pos[1], vertex.pos[2]) * transform; min_height = min(min_height, position.y); max_height = max(max_height, position.y); + min_width = min(min_width, position.x); + max_width = max(max_width, position.x); } - - material->SetProperty(MaterialProperty::WorldSpaceHeight, max_height - min_height); + + material->SetProperty(MaterialProperty::LocalWidth, max_width - min_width); + material->SetProperty(MaterialProperty::LocalHeight, max_height - min_height); } }