17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 05:09:20 +02:00

feat(renderer): a matte variant of a material for the parts of rails no wheel rolls on: it lights the same maps as weathered steel, with the weathering scaled per vertex by userdata, so a polished running surface fades into unused steel without a bright stripe; the cut faces of rails are drawn in it in an extra pass

This commit is contained in:
maj00r
2026-10-13 18:30:16 +02:00
parent 05ffb9058c
commit f067824014
7 changed files with 133 additions and 0 deletions

View File

@@ -662,6 +662,54 @@ material_manager::create_from_text( std::string const &Name, std::string const &
return materialhandle;
}
material_handle
material_manager::create_matte( material_handle const Material ) {
if( Material == null_handle || Material >= m_materials.size() ) {
return Material;
}
auto const lookup { m_mattes.find( Material ) };
if( lookup != m_mattes.end() ) {
return lookup->second;
}
auto const &source { m_materials[ Material ] };
opengl_material matte;
matte.textures = source.textures;
matte.texture_variants = source.texture_variants;
matte.params = source.params;
matte.params_state = source.params_state;
matte.shader = source.shader;
matte.opacity = source.opacity;
matte.selfillum = source.selfillum;
matte.glossiness = 0.1f;
matte.shadow_rank = source.shadow_rank;
matte.name = source.name + ":matte";
matte.size = source.size;
matte.is_good = source.is_good;
matte.path = source.path;
if( matte.shader ) {
for( auto const &[ key, value ] : std::vector<std::pair<std::string, glm::vec4>>{ { "weathering", glm::vec4{ 1.f } } } ) {
auto const entry { matte.shader->param_conf.find( key ) };
if( entry == matte.shader->param_conf.end() ) {
continue;
}
for( std::size_t i = 0; i < entry->second.size; ++i ) {
matte.params[ entry->second.location ][ entry->second.offset + i ] = value[ static_cast<glm::length_t>( i ) ];
}
matte.params_state.erase(
std::remove_if(
std::begin( matte.params_state ), std::end( matte.params_state ),
[&]( gl::shader::param_entry const &State ) {
return State.location == entry->second.location && State.offset == entry->second.offset; } ),
std::end( matte.params_state ) );
}
}
auto const mattehandle { static_cast<material_handle>( m_materials.size() ) };
m_materials.emplace_back( std::move( matte ) );
m_mattes.emplace( Material, mattehandle );
return mattehandle;
}
void
material_manager::on_weather_change() {

View File

@@ -142,6 +142,9 @@ public:
// material described by provided text (the content of a .mat file), not kept in a file. returns: handle to the material or null_handle
material_handle
create_from_text( std::string const &Name, std::string const &Definition );
// copy of provided material with its specular highlights and reflections damped. returns: handle to the copy, made once per material
material_handle
create_matte( material_handle const Material );
opengl_material const &
material( material_handle const Material ) const { return m_materials[ Material ]; }
opengl_material &
@@ -170,6 +173,7 @@ private:
// members:
material_sequence m_materials;
index_map m_materialmappings;
std::unordered_map<material_handle, material_handle> m_mattes;
};
//---------------------------------------------------------------------------

View File

@@ -2225,6 +2225,12 @@ material_handle opengl33_renderer::Fetch_Material(std::string const &Filename, b
return m_materials.create(Filename, Loadnow);
}
material_handle opengl33_renderer::Matte_Material(material_handle const Material)
{
return m_materials.create_matte(Material);
}
std::shared_ptr<gl::program> opengl33_renderer::Fetch_Shader(const std::string &name)
{
auto it = m_shaders.find(name);
@@ -4476,6 +4482,39 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
}
flush_batch();
// cut faces of rails, in matte variants of the rail materials
if ((m_renderpass.draw_mode == rendermode::color) || (m_renderpass.draw_mode == rendermode::reflections))
{
for (auto first{First}; first != Last; ++first)
{
auto const track{*first};
if (false == track->m_visible)
{
continue;
}
for (auto const &cuts : {std::make_pair(track->m_materialcut1, &track->GeometryCut1), std::make_pair(track->m_materialcut2, &track->GeometryCut2)})
{
if ((cuts.first == null_handle) || cuts.second->empty())
{
continue;
}
if (track->eEnvironment != e_flat)
{
flush_batch();
setup_environment_light(track->eEnvironment);
Bind_Material(cuts.first);
draw(std::begin(*cuts.second), std::end(*cuts.second));
setup_environment_light();
continue;
}
begin_batch(cuts.first);
m_pathbatch.insert(std::end(m_pathbatch), std::begin(*cuts.second), std::end(*cuts.second));
}
}
flush_batch();
}
// fourth pass: per-track sleeper models (sleepermodel optional directive).
// drawn after rails/trackbeds so depth pre-pass culling is favourable, and only in passes
// where Render_Sleepers actually does work (it gates itself on draw mode / distance).

View File

@@ -78,6 +78,8 @@ class opengl33_renderer : public gfx_renderer {
// material methods
material_handle
Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override;
material_handle
Matte_Material( material_handle const Material ) override;
void
Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override;
IMaterial const *

View File

@@ -3327,6 +3327,32 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First,
}
}
}
// cut faces of rails
if( ( m_renderpass.draw_mode == rendermode::color )
|| ( m_renderpass.draw_mode == rendermode::reflections ) ) {
for( auto first { First }; first != Last; ++first ) {
auto const track { *first };
if( false == track->m_visible ) {
continue;
}
for( auto const &cuts : { std::make_pair( track->m_materialcut1, &track->GeometryCut1 ), std::make_pair( track->m_materialcut2, &track->GeometryCut2 ) } ) {
if( ( cuts.first == null_handle )
|| ( cuts.second->empty() ) ) {
continue;
}
if( track->eEnvironment != e_flat ) {
setup_environment_light( track->eEnvironment );
}
Bind_Material( cuts.first );
m_geometry.draw( std::begin( *cuts.second ), std::end( *cuts.second ) );
if( track->eEnvironment != e_flat ) {
setup_environment_light();
}
}
}
}
// post-render reset
switch( m_renderpass.draw_mode ) {
case rendermode::shadows:

View File

@@ -69,6 +69,8 @@ public:
virtual auto Fetch_Material( std::string const &Filename, bool const Loadnow = true ) -> material_handle = 0;
virtual void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) = 0;
virtual auto Material( material_handle const Material ) const -> IMaterial const * = 0;
// the same material with its highlights damped, for faces which aren't polished like the surfaces they are cut from. backends which can't make one give back the material itself
virtual auto Matte_Material( material_handle const Material ) -> material_handle { return Material; }
// heightmap terrain: material of a chunk blending the materials of up to 8 layers by their weights.
// Weights: (Samples + 1)^2 values per layer, planar, rows of growing z, the weights of a sample adding up to 255; null with a single layer.
// Placement: corner of the chunk (x, z) in the texture coordinates of its mesh, and the size of the chunk.

View File

@@ -9,6 +9,8 @@ uniform sampler2D headlightmap;
float glossiness = 1.0;
float metalic = 0.0;
in vec4 UserData;
#param (weathering, 2, 3, 1, zero)
// ---------------------------------------------------------------------
// Lighting balance tunables - tweak these to control overall scene
@@ -285,6 +287,16 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl
{
// Diffuse colour is applied once, to the albedo; the light terms below are not scaled by it again
texturecolor *= material_diffuse();
float weathered = (param[2].w > 0.0 ? clamp(param[2].w * UserData.x, 0.0, 1.0) : 0.0);
float polished = weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)));
float weatheredluma = min(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 0.05);
vec3 unused = texturecolor * (weatheredluma / max(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 1e-4));
unused = mix(unused, vec3(1.0, 0.82, 0.74) * (weatheredluma / 0.8525), 0.5);
texturecolor = mix(texturecolor, unused, smoothstep(0.0, 0.4, weathered));
specularity = mix(specularity, min(specularity, 0.03), smoothstep(0.0, 0.4, polished));
metalic = mix(metalic, 0.0, polished);
glossiness = mix(glossiness, min(glossiness, 0.06 * abs(param[1].w)), polished);
reflectivity = mix(reflectivity, min(reflectivity, 0.08 * param[1].z), smoothstep(0.0, 0.4, polished));
// Scale ambient before it gets tinted by the texture.
// Sun, headlights and emission are added afterwards so they are NOT
// attenuated by AMBIENT_SCALE - this only dims the indirect term.