Skip to content

Commit

Permalink
[forest] added curved normals for the grass blades and fix a crash wh…
Browse files Browse the repository at this point in the history
…ere brixelizer gi was trying to compute gi for each grass blade
  • Loading branch information
PanosK92 committed Feb 18, 2025
1 parent 94f5dd9 commit 9b7f7a5
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 79 deletions.
5 changes: 3 additions & 2 deletions data/shaders/common_resources.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
122 changes: 60 additions & 62 deletions data/shaders/common_vertex_processing.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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;
}
};

Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions runtime/RHI/Vulkan/Vulkan_FidelityFX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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>& entity = entities[i];

// skip grass blades, it's going to bring it to a crawl
if (entity->GetComponent<Renderable>()->GetMaterial()->GetProperty(MaterialProperty::AnimationFoliageGravity))
continue;

uint64_t entity_id = entity->GetObjectId();
brixelizer_gi::entity_map[entity_id] = entity;
bool is_dynamic = entity->IsMoving();
Expand Down
4 changes: 2 additions & 2 deletions runtime/Rendering/Material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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));
}

Expand Down
3 changes: 2 additions & 1 deletion runtime/Rendering/Material.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions runtime/Rendering/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 5 additions & 4 deletions runtime/Rendering/Renderer_Buffers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 9 additions & 4 deletions runtime/World/Components/Renderable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,22 +239,27 @@ namespace spartan
m_material->PrepareForGpu();
}

// compute mesh height
// compute local dimensions
{
vector<RHI_Vertex_PosTexNorTan> 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);
}
}

Expand Down

0 comments on commit 9b7f7a5

Please sign in to comment.