mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-23 01:51:10 +02:00
feat(terrain): grass follows the seasons and fades out over gfx.grass.fadedistance
This commit is contained in:
@@ -3399,11 +3399,22 @@ void editor_mode::render_grass_ui()
|
||||
field(ImGui::DragFloat(STR_C("Density (blades/m2)"), &kind.density, 0.5f, 1.0f, 300.0f, "%.0f"));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("Blades on a square metre painted fully. The renderer draws fewer further away, and wider;\n"
|
||||
"gfx.grass.distance and gfx.grass.density in eu07.ini set how far and how many"));
|
||||
"gfx.grass.distance, gfx.grass.fadedistance and gfx.grass.density in eu07.ini set how far and how many"));
|
||||
ImGui::SetNextItemWidth(120.0f);
|
||||
field(ImGui::SliderFloat(STR_C("Lean"), &kind.bend, 0.0f, 1.0f, "%.2f"));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("How far the tips of the blades lean over, share of their height; the wind adds to it"));
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
field(ImGui::ColorEdit3(STR_C("Dried out"), &kind.dry.r, ImGuiColorEditFlags_NoInputs));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("Colour the tips take as the grass dries out, in late summer, autumn and winter"));
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(120.0f);
|
||||
field(ImGui::SliderFloat(STR_C("Follows the seasons"), &kind.seasonal, 0.0f, 1.0f, "%.2f"));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("How much the colour and the height change through the year with the date of the scenario:\n"
|
||||
"dry and low in winter, greening up in spring, fresh in May, drying out from August.\n"
|
||||
"0: the same all year (e.g. a watered lawn), 1: fully"));
|
||||
m_grass_edit_active = active;
|
||||
if (changed)
|
||||
{
|
||||
|
||||
@@ -800,9 +800,11 @@ void terrain_streamer::publish_grass(glm::dvec3 const &Camera)
|
||||
entry.width = kind.width;
|
||||
entry.density = kind.density;
|
||||
entry.bend = kind.bend;
|
||||
entry.dry = glm::pow(glm::clamp(kind.dry, glm::vec3{0.f}, glm::vec3{1.f}), glm::vec3{2.2f});
|
||||
entry.seasonal = kind.seasonal;
|
||||
kinds.push_back(entry);
|
||||
}
|
||||
auto const reach{static_cast<double>(Global.gfx_grass_distance) + 2.0};
|
||||
auto const reach{static_cast<double>(std::max(Global.gfx_grass_distance, Global.gfx_grass_fadedistance)) + 2.0};
|
||||
for (auto const &entry : m_chunks)
|
||||
{
|
||||
auto const &chunk{*entry.second};
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
@@ -9964,8 +9964,8 @@ msgstr "Dodaj:"
|
||||
msgid "Blade width (m)"
|
||||
msgstr "Szerokość źdźbła (m)"
|
||||
|
||||
msgid "Blades on a square metre painted fully. The renderer draws fewer further away, and wider;\ngfx.grass.distance and gfx.grass.density in eu07.ini set how far and how many"
|
||||
msgstr "Źdźbła na metrze kwadratowym w pełni pomalowanym. Dalej renderer rysuje ich mniej, ale szerszych;\ngfx.grass.distance i gfx.grass.density w eu07.ini ustalają, jak daleko i ile"
|
||||
msgid "Blades on a square metre painted fully. The renderer draws fewer further away, and wider;\ngfx.grass.distance, gfx.grass.fadedistance and gfx.grass.density in eu07.ini set how far and how many"
|
||||
msgstr "Źdźbła na metrze kwadratowym w pełni pomalowanym. Dalej renderer rysuje ich mniej, ale szerszych;\ngfx.grass.distance, gfx.grass.fadedistance i gfx.grass.density w eu07.ini ustalają, jak daleko i ile"
|
||||
|
||||
msgid "Clear the grass"
|
||||
msgstr "Usuń trawę"
|
||||
@@ -10074,3 +10074,15 @@ msgstr "wysoka trawa"
|
||||
|
||||
msgid "weeds"
|
||||
msgstr "chwasty"
|
||||
|
||||
msgid "Dried out"
|
||||
msgstr "Wyschnięta"
|
||||
|
||||
msgid "Colour the tips take as the grass dries out, in late summer, autumn and winter"
|
||||
msgstr "Kolor, jaki przybierają czubki, gdy trawa wysycha: późnym latem, jesienią i zimą"
|
||||
|
||||
msgid "Follows the seasons"
|
||||
msgstr "Zmienia się z porami roku"
|
||||
|
||||
msgid "How much the colour and the height change through the year with the date of the scenario:\ndry and low in winter, greening up in spring, fresh in May, drying out from August.\n0: the same all year (e.g. a watered lawn), 1: fully"
|
||||
msgstr "Jak bardzo kolor i wysokość zmieniają się w ciągu roku z datą scenariusza:\nzimą sucha i niska, wiosną zielenieje, w maju świeża, od sierpnia wysycha.\n0: taka sama przez cały rok (np. podlewany trawnik), 1: w pełni"
|
||||
|
||||
@@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/openglcamera.h"
|
||||
#include "model/Texture.h"
|
||||
#include "simulation/simulationenvironment.h"
|
||||
#include "simulation/simulationtime.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/Timer.h"
|
||||
@@ -23,10 +24,37 @@ namespace
|
||||
int constexpr max_blades{65536}; // drawn in a tile at most
|
||||
float constexpr blade_reach{1.5f}; // metres the bounds of a tile reach above its ground, for the blades
|
||||
|
||||
// the share of the blades drawn at specified distance; the same as lod_share() of grass.vert
|
||||
float lod_share(float const Distance, float const Near)
|
||||
// the share of the blades drawn at specified distance: fewer further away, for speed (from Thinning), and fewer and fewer from
|
||||
// Fade to Reach, where the grass ends; the same as lod_share() of grass.vert
|
||||
float lod_share(float const Distance, float const Thinning, float const Fade, float const Reach)
|
||||
{
|
||||
return Distance <= Near ? 1.f : std::pow(Near / Distance, 1.5f);
|
||||
auto const share{Distance <= Thinning ? 1.f : std::pow(Thinning / Distance, 1.5f)};
|
||||
auto const t{std::clamp((Distance - Fade) / std::max(Reach - Fade, 1e-3f), 0.f, 1.f)};
|
||||
return share * (1.f - t * t * (3.f - 2.f * t));
|
||||
}
|
||||
|
||||
// the grass through the year, by the day of the year: x how dry it is, y how fresh (the green of spring), z how grown (share
|
||||
// of the height). the turns follow the seasons of the textures (world_environment::compute_season(): winter till day 65,
|
||||
// spring till 158, summer till 252, autumn till 341): dormant and dry in winter, greening up and growing through spring,
|
||||
// fresh in May, drying out from August, the dry grass lying lower towards winter
|
||||
glm::vec3 grass_season(int const Yearday)
|
||||
{
|
||||
struct point
|
||||
{
|
||||
float day;
|
||||
glm::vec3 value;
|
||||
};
|
||||
static point const year[]{{1.f, {0.80f, 0.f, 0.55f}}, {65.f, {0.80f, 0.f, 0.55f}}, {100.f, {0.45f, 0.3f, 0.70f}}, {135.f, {0.05f, 1.f, 0.90f}},
|
||||
{170.f, {0.f, 0.4f, 1.f}}, {200.f, {0.10f, 0.f, 1.f}}, {240.f, {0.35f, 0.f, 1.f}}, {280.f, {0.50f, 0.f, 0.95f}},
|
||||
{320.f, {0.65f, 0.f, 0.80f}}, {341.f, {0.80f, 0.f, 0.60f}}, {367.f, {0.80f, 0.f, 0.55f}}};
|
||||
auto const day{std::clamp(static_cast<float>(Yearday), 1.f, 366.f)};
|
||||
for (std::size_t i = 1; i < std::size(year); ++i)
|
||||
if (day <= year[i].day)
|
||||
{
|
||||
auto const t{(day - year[i - 1].day) / (year[i].day - year[i - 1].day)};
|
||||
return glm::mix(year[i - 1].value, year[i].value, t * t * (3.f - 2.f * t));
|
||||
}
|
||||
return year[std::size(year) - 1].value;
|
||||
}
|
||||
|
||||
GLuint make_texture(GLenum const Format, int const Width, int const Height, GLenum const Filter)
|
||||
@@ -78,6 +106,8 @@ bool opengl46_grass::init()
|
||||
m_uniforms.tip = location("grass_tip");
|
||||
m_uniforms.shape = location("grass_shape");
|
||||
m_uniforms.share = location("grass_share");
|
||||
m_uniforms.dry = location("grass_dry");
|
||||
m_uniforms.season = location("grass_season");
|
||||
m_state = 1;
|
||||
return true;
|
||||
}
|
||||
@@ -207,15 +237,19 @@ std::size_t opengl46_grass::render(opengl_camera const &Camera, glm::mat4 const
|
||||
if (false == Global.gfx_grass || m_chunks.empty() || m_kinds.empty() || false == init())
|
||||
return 0;
|
||||
|
||||
auto const reach{std::max(5.f, Global.gfx_grass_distance)};
|
||||
auto const thinning{std::max(3.f, reach * 0.2f)};
|
||||
// whole up to the distance, thinning out to the fading distance
|
||||
auto const fade{std::max(5.f, Global.gfx_grass_distance)};
|
||||
auto const reach{std::max(fade + 1.f, Global.gfx_grass_fadedistance)};
|
||||
auto const thinning{std::max(3.f, fade * 0.2f)};
|
||||
auto const multiplier{std::max(0.f, Global.gfx_grass_density)};
|
||||
auto const camera{Camera.position()};
|
||||
|
||||
m_program->bind();
|
||||
m_vao->bind();
|
||||
::glDisable(GL_CULL_FACE);
|
||||
::glUniform4f(m_uniforms.lod, thinning, reach, 0.f, 0.f);
|
||||
::glUniform4f(m_uniforms.lod, thinning, fade, reach, 0.f);
|
||||
auto const season{grass_season(simulation::Time.year_day())};
|
||||
::glUniform4f(m_uniforms.season, season.x, season.y, season.z, 0.f);
|
||||
// the wind sways the blades along it, harder in the gusts
|
||||
auto const wind{glm::vec3{simulation::Environment.wind()}};
|
||||
auto const windspeed{glm::length(glm::vec2{wind.x, wind.z})};
|
||||
@@ -227,7 +261,7 @@ std::size_t opengl46_grass::render(opengl_camera const &Camera, glm::mat4 const
|
||||
{
|
||||
auto &chunk{entry.second};
|
||||
// the kinds of the chunk, and the share of the blades of each against its densest
|
||||
std::array<glm::vec4, 4> base{}, tip{}, shape{};
|
||||
std::array<glm::vec4, 4> base{}, tip{}, shape{}, dry{};
|
||||
glm::vec4 share{0.f};
|
||||
float densest{0.f};
|
||||
for (std::size_t i = 0; i < chunk.kinds.size(); ++i)
|
||||
@@ -243,6 +277,7 @@ std::size_t opengl46_grass::render(opengl_camera const &Camera, glm::mat4 const
|
||||
base[i] = glm::vec4{kind.base, 0.f};
|
||||
tip[i] = glm::vec4{kind.tip, 0.f};
|
||||
shape[i] = glm::vec4{kind.height, kind.variation, kind.width, kind.bend};
|
||||
dry[i] = glm::vec4{kind.dry, kind.seasonal};
|
||||
share[i] = kind.density / densest;
|
||||
}
|
||||
auto const tallest{std::max({shape[0].x, shape[1].x, shape[2].x, shape[3].x}) * 1.5f + blade_reach};
|
||||
@@ -266,7 +301,7 @@ std::size_t opengl46_grass::render(opengl_camera const &Camera, glm::mat4 const
|
||||
if (false == Camera.visible(scene::bounding_area{(low + high) * 0.5, static_cast<float>(glm::length(high - low) * 0.5)}))
|
||||
continue;
|
||||
auto const full{tilesize * tilesize * densest * multiplier * fill};
|
||||
auto const count{std::min(max_blades, static_cast<int>(std::ceil(full * lod_share(distance, thinning))))};
|
||||
auto const count{std::min(max_blades, static_cast<int>(std::ceil(full * lod_share(distance, thinning, fade, reach))))};
|
||||
if (count <= 0)
|
||||
continue;
|
||||
if (false == set)
|
||||
@@ -280,11 +315,12 @@ std::size_t opengl46_grass::render(opengl_camera const &Camera, glm::mat4 const
|
||||
::glUniform4fv(m_uniforms.tip, 4, glm::value_ptr(tip[0]));
|
||||
::glUniform4fv(m_uniforms.shape, 4, glm::value_ptr(shape[0]));
|
||||
::glUniform4fv(m_uniforms.share, 1, glm::value_ptr(share));
|
||||
::glUniform4fv(m_uniforms.dry, 4, glm::value_ptr(dry[0]));
|
||||
GLuint const textures[]{chunk.heights, chunk.density, chunk.holes};
|
||||
::glBindTextures(0, 3, textures);
|
||||
}
|
||||
// fewer segments further away
|
||||
auto const segments{distance < thinning * 1.5f ? 3 : distance < reach * 0.5f ? 2 : 1};
|
||||
auto const segments{distance < thinning * 1.5f ? 3 : distance < fade * 0.5f ? 2 : 1};
|
||||
// the number of the tile in the world, the seed of its blades
|
||||
auto const seedx{static_cast<float>(std::floor(chunk.corner.x / tilesize + 0.5) + tx)};
|
||||
auto const seedz{static_cast<float>(std::floor(chunk.corner.y / tilesize + 0.5) + tz)};
|
||||
|
||||
@@ -80,6 +80,6 @@ class opengl46_grass
|
||||
struct locations
|
||||
{
|
||||
GLint tile{-1}, part{-1}, chunk{-1}, camera{-1}, lod{-1}, wind{-1};
|
||||
GLint base{-1}, tip{-1}, shape{-1}, share{-1};
|
||||
GLint base{-1}, tip{-1}, shape{-1}, share{-1}, dry{-1}, season{-1};
|
||||
} m_uniforms;
|
||||
};
|
||||
|
||||
@@ -1111,7 +1111,8 @@ void opengl46_renderer::draw_debug_ui()
|
||||
ImGui::Checkbox("grass of the heightmap terrain (gfx.grass)", &Global.gfx_grass);
|
||||
if (Global.gfx_grass)
|
||||
{
|
||||
ImGui::SliderFloat("reach of the grass, m (gfx.grass.distance)", &Global.gfx_grass_distance, 10.f, 250.f, "%.0f");
|
||||
ImGui::SliderFloat("the grass drawn whole up to, m (gfx.grass.distance)", &Global.gfx_grass_distance, 10.f, 300.f, "%.0f");
|
||||
ImGui::SliderFloat("thinning out up to, m (gfx.grass.fadedistance)", &Global.gfx_grass_fadedistance, 10.f, 350.f, "%.0f");
|
||||
ImGui::SliderFloat("density of the grass (gfx.grass.density)", &Global.gfx_grass_density, 0.1f, 2.f, "%.2f");
|
||||
}
|
||||
auto info{m_instancecull.info()};
|
||||
|
||||
@@ -44,6 +44,8 @@ struct grass_kind
|
||||
float width{0.012f}; // metres, at the root
|
||||
float density{60.f}; // blades per square metre where painted fully
|
||||
float bend{0.3f}; // share of the height the tips lean over by
|
||||
glm::vec3 dry{0.f}; // the tips dried out, in autumn and winter
|
||||
float seasonal{1.f}; // how much the kind follows the seasons, 0..1
|
||||
};
|
||||
|
||||
// a chunk of the heightmap terrain the grass grows on, see gfx_renderer::Terrain_Grass()
|
||||
|
||||
@@ -656,12 +656,12 @@ std::optional<modifier_shape::effect> modifier_shape::evaluate(double const X, d
|
||||
std::vector<grass_def> const &grass_presets()
|
||||
{
|
||||
static std::vector<grass_def> const presets{
|
||||
// name, root, tip, height, variation, width, density, bend
|
||||
{"meadow", {0.22f, 0.33f, 0.09f}, {0.58f, 0.68f, 0.27f}, 0.25f, 0.45f, 0.020f, 90.f, 0.30f},
|
||||
{"lawn", {0.18f, 0.30f, 0.08f}, {0.42f, 0.58f, 0.20f}, 0.07f, 0.30f, 0.012f, 200.f, 0.15f},
|
||||
{"dry grass", {0.36f, 0.32f, 0.16f}, {0.80f, 0.72f, 0.45f}, 0.30f, 0.45f, 0.016f, 70.f, 0.40f},
|
||||
{"tall grass", {0.18f, 0.27f, 0.07f}, {0.60f, 0.66f, 0.30f}, 0.70f, 0.35f, 0.022f, 40.f, 0.45f},
|
||||
{"weeds", {0.16f, 0.25f, 0.08f}, {0.45f, 0.55f, 0.22f}, 0.16f, 0.60f, 0.030f, 30.f, 0.25f},
|
||||
// name, root, tip, height, variation, width, density, bend, dried out, follows the seasons
|
||||
{"meadow", {0.22f, 0.33f, 0.09f}, {0.58f, 0.68f, 0.27f}, 0.25f, 0.45f, 0.020f, 90.f, 0.30f, {0.74f, 0.66f, 0.40f}, 1.f},
|
||||
{"lawn", {0.18f, 0.30f, 0.08f}, {0.42f, 0.58f, 0.20f}, 0.07f, 0.30f, 0.012f, 200.f, 0.15f, {0.62f, 0.60f, 0.36f}, 0.6f},
|
||||
{"dry grass", {0.36f, 0.32f, 0.16f}, {0.80f, 0.72f, 0.45f}, 0.30f, 0.45f, 0.016f, 70.f, 0.40f, {0.82f, 0.74f, 0.50f}, 0.5f},
|
||||
{"tall grass", {0.18f, 0.27f, 0.07f}, {0.60f, 0.66f, 0.30f}, 0.70f, 0.35f, 0.022f, 40.f, 0.45f, {0.76f, 0.66f, 0.40f}, 1.f},
|
||||
{"weeds", {0.16f, 0.25f, 0.08f}, {0.45f, 0.55f, 0.22f}, 0.16f, 0.60f, 0.030f, 30.f, 0.25f, {0.52f, 0.44f, 0.28f}, 1.f},
|
||||
};
|
||||
return presets;
|
||||
}
|
||||
@@ -762,6 +762,9 @@ bool load_manifest(std::string const &Path, manifest &Manifest, std::string *Err
|
||||
kind.density = std::max(0.f, density.as<float>());
|
||||
if (auto const bend{entry["bend"]})
|
||||
kind.bend = std::clamp(bend.as<float>(), 0.f, 1.f);
|
||||
colour(entry["dry"], kind.dry);
|
||||
if (auto const seasonal{entry["seasonal"]})
|
||||
kind.seasonal = std::clamp(seasonal.as<float>(), 0.f, 1.f);
|
||||
result.grass.push_back(std::move(kind));
|
||||
}
|
||||
Manifest = std::move(result);
|
||||
@@ -852,6 +855,8 @@ bool save_manifest(std::string const &Path, manifest const &Manifest, std::strin
|
||||
out << YAML::Key << "width" << YAML::Value << kind.width;
|
||||
out << YAML::Key << "density" << YAML::Value << kind.density;
|
||||
out << YAML::Key << "bend" << YAML::Value << kind.bend;
|
||||
out << YAML::Key << "dry" << YAML::Value << YAML::Flow << YAML::BeginSeq << kind.dry.r << kind.dry.g << kind.dry.b << YAML::EndSeq;
|
||||
out << YAML::Key << "seasonal" << YAML::Value << kind.seasonal;
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
out << YAML::EndSeq;
|
||||
|
||||
@@ -222,6 +222,8 @@ struct grass_def
|
||||
float width{0.02f}; // metres, of a blade at its root
|
||||
float density{90.f}; // blades per square metre where the kind is painted fully
|
||||
float bend{0.3f}; // how far the tips lean over, share of the height
|
||||
glm::vec3 dry{0.74f, 0.66f, 0.40f}; // colour of the tips dried out, in autumn and winter, sRGB 0..1
|
||||
float seasonal{1.f}; // how much the kind follows the seasons: 0 the same all year, 1 fully
|
||||
};
|
||||
|
||||
// a few kinds to start the palette with
|
||||
|
||||
@@ -18,7 +18,8 @@ uniform vec4 grass_part;
|
||||
uniform vec4 grass_chunk;
|
||||
// the camera relative to the corner of the chunk (y as it is)
|
||||
uniform vec4 grass_camera;
|
||||
// x: distance the grass starts to thin out at; y: reach of the grass
|
||||
// x: distance the grass starts to thin out at, for speed; y: distance the fading starts at (gfx.grass.distance); z: where the
|
||||
// grass ends (gfx.grass.fadedistance)
|
||||
uniform vec4 grass_lod;
|
||||
// x, y: direction of the wind (x, z of the world); z: its strength, 0..1; w: time, seconds
|
||||
uniform vec4 grass_wind;
|
||||
@@ -28,6 +29,10 @@ uniform vec4 grass_tip[4];
|
||||
uniform vec4 grass_shape[4];
|
||||
// share of the blades of each kind against the densest one of the chunk
|
||||
uniform vec4 grass_share;
|
||||
// the colour of each kind dried out (linear rgb), and how much it follows the seasons
|
||||
uniform vec4 grass_dry[4];
|
||||
// the time of the year: x: how dry the grass is, y: how fresh (spring), z: how grown (share of the height)
|
||||
uniform vec4 grass_season;
|
||||
|
||||
layout(binding = 0) uniform sampler2D grass_heights; // heights of the grid points, R32F
|
||||
layout(binding = 1) uniform sampler2D grass_density; // densities of the kinds, RGBA8
|
||||
@@ -56,12 +61,19 @@ float random(inout uint state)
|
||||
return float(state >> 8) * (1.0 / 16777216.0);
|
||||
}
|
||||
|
||||
// the share of the blades drawn at specified distance; the same as lod_share() of opengl46grass.cpp
|
||||
float lod_share(float distance)
|
||||
// the share of the blades drawn at specified distance for speed: fewer further away, each standing for more of them
|
||||
float lod_thinning(float distance)
|
||||
{
|
||||
return distance <= grass_lod.x ? 1.0 : pow(grass_lod.x / distance, 1.5);
|
||||
}
|
||||
|
||||
// the share of the blades drawn: the above, and fewer and fewer over the fading distance, where the grass really gets
|
||||
// sparser; the same as lod_share() of opengl46grass.cpp
|
||||
float lod_share(float distance)
|
||||
{
|
||||
return lod_thinning(distance) * (1.0 - smoothstep(grass_lod.y, grass_lod.z, distance));
|
||||
}
|
||||
|
||||
// height of the ground at a point of the chunk and its normal, the way the full grid is triangulated
|
||||
float ground(vec2 point, out vec3 normal)
|
||||
{
|
||||
@@ -111,6 +123,7 @@ void main()
|
||||
float leanamount = random(state);
|
||||
float shade = random(state);
|
||||
float phase = random(state);
|
||||
float early = random(state);
|
||||
|
||||
// place: R2 spreads the first blades evenly at any count, a little jitter breaks its lattice
|
||||
vec2 spot = fract(shift + float(blade) * vec2(0.7548776662466927, 0.5698402909980532));
|
||||
@@ -148,7 +161,7 @@ void main()
|
||||
float distance = length(root - grass_camera.xyz);
|
||||
float share = lod_share(distance);
|
||||
float rank = (float(blade) + 0.5) / grass_tile.w;
|
||||
if (kind < 0 || hole > 0.5 || rank >= share || distance > grass_lod.y)
|
||||
if (kind < 0 || hole > 0.5 || rank >= share || distance > grass_lod.z)
|
||||
{
|
||||
collapse();
|
||||
return;
|
||||
@@ -158,11 +171,13 @@ void main()
|
||||
float height = shape.x * (1.0 + shape.y * heightrandom);
|
||||
// the last of the blades drawn at the distance grow in, and the grass fades out at the end of its reach
|
||||
height *= 1.0 - smoothstep(share * 0.8, share, rank);
|
||||
height *= 1.0 - smoothstep(grass_lod.y * 0.75, grass_lod.y, distance);
|
||||
// lower in winter, growing in spring
|
||||
float seasonal = grass_dry[kind].a;
|
||||
height *= mix(1.0, grass_season.z, seasonal);
|
||||
// where the paint thins out the blades get shorter
|
||||
height *= mix(0.5, 1.0, clamp(density[kind] * 1.5, 0.0, 1.0));
|
||||
// the blades drawn further away stand for more of them, so they're wider
|
||||
float width = shape.z * (0.8 + 0.4 * widthrandom) / sqrt(share);
|
||||
float width = shape.z * (0.8 + 0.4 * widthrandom) / sqrt(lod_thinning(distance));
|
||||
|
||||
// the face of the blade turns towards the camera further away, so the blades don't vanish edge on
|
||||
vec2 facing = vec2(cos(turnrandom), sin(turnrandom));
|
||||
@@ -192,7 +207,13 @@ void main()
|
||||
|
||||
// lit mostly as the ground under it, which keeps a field of blades from shimmering
|
||||
vec3 normal = normalize(groundnormal * 0.8 + vec3(facing.x, 0.0, facing.y) * (side == 0.0 ? 0.0 : 0.25) + vec3(0.0, 0.3, 0.0));
|
||||
vec3 albedo = mix(grass_base[kind].rgb, grass_tip[kind].rgb, t) * (0.85 + 0.3 * shade);
|
||||
// the seasons: some blades dry out (and green up) earlier than others, so a field turns gradually; a dry blade takes the
|
||||
// dry colour, darker at its root. in spring the green is fresher
|
||||
float dry = clamp(grass_season.x * seasonal * (0.6 + 0.8 * early), 0.0, 1.0);
|
||||
vec3 bottom = mix(grass_base[kind].rgb, grass_dry[kind].rgb * 0.45, dry);
|
||||
vec3 top = mix(grass_tip[kind].rgb, grass_dry[kind].rgb, dry);
|
||||
top *= mix(vec3(1.0), vec3(1.08, 1.22, 0.85), grass_season.y * seasonal * (1.0 - dry));
|
||||
vec3 albedo = mix(bottom, top, t) * (0.85 + 0.3 * shade);
|
||||
// the lower part of the blades is shaded by the others
|
||||
albedo *= mix(0.7, 1.0, t);
|
||||
|
||||
|
||||
@@ -1539,6 +1539,12 @@ global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token
|
||||
Parser >> gfx_grass_distance;
|
||||
gfx_grass_distance = std::clamp(gfx_grass_distance, 5.f, 500.f);
|
||||
}
|
||||
else if (Token == "gfx.grass.fadedistance")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
Parser >> gfx_grass_fadedistance;
|
||||
gfx_grass_fadedistance = std::clamp(gfx_grass_fadedistance, 5.f, 600.f);
|
||||
}
|
||||
else if (Token == "gfx.grass.density")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
@@ -2060,6 +2066,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.depthprepass.replay", gfx_depthprepass_replay );
|
||||
export_as_text( Output, "gfx.grass", gfx_grass );
|
||||
export_as_text( Output, "gfx.grass.distance", gfx_grass_distance );
|
||||
export_as_text( Output, "gfx.grass.fadedistance", gfx_grass_fadedistance );
|
||||
export_as_text( Output, "gfx.grass.density", gfx_grass_density );
|
||||
export_as_text( Output, "gfx.bufferstreaming", gfx_bufferstreaming );
|
||||
export_as_text( Output, "gfx.postfx.motionblur.enabled", gfx_postfx_motionblur_enabled );
|
||||
|
||||
@@ -325,6 +325,8 @@ struct global_settings {
|
||||
// many blades of the density painted (1: all)
|
||||
bool gfx_grass = true;
|
||||
float gfx_grass_distance = 60.f;
|
||||
// (opengl 4.6) the grass thins out from gfx_grass_distance to here, where it ends
|
||||
float gfx_grass_fadedistance = 70.f;
|
||||
float gfx_grass_density = 1.f;
|
||||
// (opengl 4.6) the model block of every draw and the matrices of instances are written to persistently mapped buffers,
|
||||
// one for each of the last frames, instead of with glBufferSubData. read at the start
|
||||
|
||||
Reference in New Issue
Block a user