diff --git a/model/material.cpp b/model/material.cpp index a791696d..46a26781 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -763,12 +763,14 @@ namespace { char const *const blend_normal_shaders[] = { "normalmap", "sunlessnormalmap", "shadowlessnormalmap" }; char const *const blend_specgloss_shaders[] = { "normalmap_specgloss", "sunlessnormalmap_specgloss", "shadowlessnormalmap_specgloss" }; int constexpr blend_specgloss { 4 }; // added to the lighting for a material with a specgloss map +int constexpr blend_parallax { 8 }; // added to the lighting (1, normalmap) of a parallax material std::string blend_shader( int const Lighting, bool const Grain ) { std::string name { "trackblend" }; if( Lighting & blend_specgloss ) { name += "_specgloss"; } + else if( Lighting & blend_parallax ) { name += "_parallax"; } else if( Lighting > 0 ) { name += "_normalmap"; } return ( Grain ? name + "_grain" : name ); } @@ -809,6 +811,9 @@ material_manager::blend_lighting( material_handle const Material ) { } else if( conf.size() == 2 && has( "normalmap" ) ) { lit_as( blend_normal_shaders, 0 ); + if( material.shader == GfxRenderer->Fetch_Shader( "parallax" ) ) { + lighting = 1 + blend_parallax; + } } else if( conf.size() == 3 && has( "normalmap" ) && has( "specgloss" ) ) { lit_as( blend_specgloss_shaders, blend_specgloss ); @@ -840,7 +845,7 @@ material_manager::blend_lighting( material_handle const Material ) { WriteLog( "track blend: material \"" + material.name + "\" isn't blended:" + ( material.is_good ? "" : " not good;" ) - + ( material.shader ? " a shader with the textures " + textures + ";" : " no shader;" ) + + ( material.shader ? " shader \"" + GfxRenderer->Shader_Name( material.shader ) + "\" with the textures " + textures + ";" : " no shader;" ) + ( material.texture_variants.empty() ? "" : " texture variants;" ) + ( material.params[ 2 ].w < 0.f ? " tinted;" : "" ) ); } @@ -872,7 +877,7 @@ material_manager::build_blend( blend_key const &Key, opengl_material &Blend ) { auto const found { source.shader->texture_conf.find( Name ) }; return ( found != source.shader->texture_conf.end() ? source.textures[ found->second.id ] : null_handle ); }; - // the slots of the textures in the blending shaders (mat_trackblend*.frag): with a specgloss map there's room for the textures + // the slots of the textures in the blending shaders (mat_trackblend*.frag), parallax as normalmap: with a specgloss map there's room for the textures // of two ends alone, p1 and p2, which is all the rails of a plain track have auto const normals { lighting > 0 }; auto const specgloss { ( lighting & blend_specgloss ) != 0 }; diff --git a/model/material.h b/model/material.h index 4133a366..42c71800 100644 --- a/model/material.h +++ b/model/material.h @@ -153,7 +153,7 @@ public: material_handle create_blend( material_handle const Material, std::array const &Others, bool const Grain ); // whether the material can be blended so: a diffuse texture lit as mat_default, or with a normal map (and a specgloss map) - // lit as normalmap, sunlessnormalmap or shadowlessnormalmap (and their _specgloss); not tinted. the blending materials take the + // lit as normalmap, sunlessnormalmap or shadowlessnormalmap (and their _specgloss), or parallax; not tinted. the blending materials take the // textures of another season or weather with the materials they blend bool blendable( material_handle const Material ); @@ -179,7 +179,7 @@ private: material_handle find_in_databank( std::string const &Materialname ) const; // how the material is lit, of the materials a track blends: 0 as mat_default, 1 normalmap, 2 sunlessnormalmap, - // 3 shadowlessnormalmap, 4 more for their _specgloss; -1 where it can't be blended + // 3 shadowlessnormalmap, 4 more for their _specgloss, 9 parallax; -1 where it can't be blended int blend_lighting( material_handle const Material ); // the blending material made of the current textures of the materials. returns: false when they can't be blended diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index 474fc398..926404f0 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -2860,6 +2860,14 @@ bool opengl46_renderer::Blendable_Material(material_handle const Material) return m_materials.blendable(Material); } +std::string opengl46_renderer::Shader_Name(std::shared_ptr const &Program) const +{ + for (auto const &shader : m_shaders) + if (shader.second == Program) + return shader.first; + return {}; +} + std::shared_ptr opengl46_renderer::Fetch_Shader(const std::string &name) { auto it = m_shaders.find(name); @@ -4396,7 +4404,8 @@ int opengl46_renderer::Prepass_Class( material_handle const Material ) "default", "default_1", "default_2", "default_detail", "default_specgloss", "detail_normalmap", "detail_normalmap_specgloss", "normalmap", "normalmap_specgloss", "reflmap", "reflmap_specgloss", "shadowlessnormalmap", "shadowlessnormalmap_specgloss", - "sunlessnormalmap", "sunlessnormalmap_specgloss" }; + "sunlessnormalmap", "sunlessnormalmap_specgloss", + "trackblend", "trackblend_grain", "trackblend_normalmap", "trackblend_normalmap_grain", "trackblend_specgloss", "trackblend_specgloss_grain" }; static std::unordered_set const solid { "colored", "default_0", "terrain", "water", "water_specgloss", "invalid" }; auto prepassclass { 0 }; for( auto const &shader : m_shaders ) { diff --git a/rendering/opengl46/opengl46renderer.h b/rendering/opengl46/opengl46renderer.h index 1fa3f091..44bc7efe 100644 --- a/rendering/opengl46/opengl46renderer.h +++ b/rendering/opengl46/opengl46renderer.h @@ -118,6 +118,7 @@ class opengl46_renderer : public gfx_renderer { Terrain_Grass( std::vector const &Kinds, std::vector const &Chunks ) override; // shader methods auto Fetch_Shader( std::string const &name ) -> std::shared_ptr override; + auto Shader_Name( std::shared_ptr const &Program ) const -> std::string override; // texture methods texture_handle Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) override; diff --git a/rendering/renderer.h b/rendering/renderer.h index ca18b13b..037be58c 100644 --- a/rendering/renderer.h +++ b/rendering/renderer.h @@ -133,6 +133,8 @@ public: virtual void Terrain_Grass( std::vector const &Kinds, std::vector const &Chunks ) {} // shader methods virtual auto Fetch_Shader( std::string const &name ) -> std::shared_ptr = 0; + // the name the program was fetched by, empty where it wasn't fetched by Fetch_Shader() + virtual auto Shader_Name( std::shared_ptr const &Program ) const -> std::string { return {}; } // texture methods virtual auto Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) -> texture_handle = 0; virtual void Bind_Texture( texture_handle const Texture ) = 0; diff --git a/shaders/mat_trackblend_parallax.frag b/shaders/mat_trackblend_parallax.frag new file mode 100644 index 00000000..42e07e86 --- /dev/null +++ b/shaders/mat_trackblend_parallax.frag @@ -0,0 +1,22 @@ +// the rails of a track of a parallax material faded into those of the tracks joined at its ends, see trackblend.glsl +#define TRACKBLEND_NORMALMAP 1 +#define TRACKBLEND_PARALLAX 1 +#param (height_scale, 2, 1, 1, zero) +#param (height_offset, 2, 2, 1, zero) +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (diffuse3, 3, sRGB_A) +uniform sampler2D diffuse3; +#texture (normalmap, 4, RGBA) +uniform sampler2D normalmap; +#texture (normalmap1, 5, RGBA) +uniform sampler2D normalmap1; +#texture (normalmap2, 6, RGBA) +uniform sampler2D normalmap2; +#texture (normalmap3, 7, RGBA) +uniform sampler2D normalmap3; +#include diff --git a/shaders/mat_trackblend_parallax_grain.frag b/shaders/mat_trackblend_parallax_grain.frag new file mode 100644 index 00000000..5db1da68 --- /dev/null +++ b/shaders/mat_trackblend_parallax_grain.frag @@ -0,0 +1,23 @@ +// the ballast of a track of a parallax material mixed in grains into that of the tracks joined at its ends, see trackblend.glsl +#define TRACKBLEND_GRAIN 1 +#define TRACKBLEND_NORMALMAP 1 +#define TRACKBLEND_PARALLAX 1 +#param (height_scale, 2, 1, 1, zero) +#param (height_offset, 2, 2, 1, zero) +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (diffuse3, 3, sRGB_A) +uniform sampler2D diffuse3; +#texture (normalmap, 4, RGBA) +uniform sampler2D normalmap; +#texture (normalmap1, 5, RGBA) +uniform sampler2D normalmap1; +#texture (normalmap2, 6, RGBA) +uniform sampler2D normalmap2; +#texture (normalmap3, 7, RGBA) +uniform sampler2D normalmap3; +#include diff --git a/shaders/trackblend.glsl b/shaders/trackblend.glsl index 6055760d..b3d6c3cd 100644 --- a/shaders/trackblend.glsl +++ b/shaders/trackblend.glsl @@ -6,12 +6,16 @@ // mat_default, 1 normalmap, 2 sunlessnormalmap, 3 shadowlessnormalmap // TRACKBLEND_SPECGLOSS the specgloss map of the track itself as well, the textures of the ends p1 and p2 alone (no room // for a third set of them) +// TRACKBLEND_PARALLAX with the normal maps, as mat_parallax: each texture shifted by the heights in its own normal map in vec3 f_normal; in vec2 f_coord; in vec4 f_pos; #ifdef TRACKBLEND_NORMALMAP in mat3 f_tbn; #endif +#ifdef TRACKBLEND_PARALLAX +in vec3 TangentFragPos; +#endif in vec4 f_clip_pos; in vec4 f_clip_future_pos; @@ -77,6 +81,49 @@ float grain_value(vec2 p) } #endif +#ifdef TRACKBLEND_PARALLAX +// the texture coordinates shifted by the heights (b) of the normal map, as ParallaxMapping() of mat_parallax.frag +vec2 parallax_coord(sampler2D heights, vec2 coord, vec3 viewdir, vec2 dcoorddx, vec2 dcoorddy) +{ + float pos_len = length(f_pos.xyz); + if (pos_len > 100.0) + return coord; + float fade = 1.0 - smoothstep(80.0, 100.0, pos_len); +#if EXTRAEFFECTS_ENABLED + const float minLayers = 8.0; + const float maxLayers = 32.0; + float LayersWeight = pos_len / 20.0; + vec2 currentTexCoords = coord; + float currentDepthMapValue = textureGrad(heights, currentTexCoords, dcoorddx, dcoorddy).b; + LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewdir)), LayersWeight); + float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); + float layerDepth = 1.0 / numLayers; + float currentLayerDepth = 0.0; + vec2 P = viewdir.xy * param[2].y * fade; + vec2 deltaTexCoords = P / numLayers; + while (currentLayerDepth < currentDepthMapValue) + { + currentTexCoords -= deltaTexCoords; + currentDepthMapValue = textureGrad(heights, currentTexCoords, dcoorddx, dcoorddy).b; + currentLayerDepth += layerDepth; + } + vec2 prevTexCoords = currentTexCoords + deltaTexCoords; + float afterDepth = currentDepthMapValue - currentLayerDepth; + float beforeDepth = textureGrad(heights, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth; + float weight = afterDepth / (afterDepth - beforeDepth); + return prevTexCoords * weight + currentTexCoords * (1.0 - weight); +#else + float height = textureGrad(heights, coord, dcoorddx, dcoorddy).b; + vec2 p = viewdir.xy / viewdir.z * (height * (param[2].y - param[2].z) * 0.2) * fade; + return coord - p; +#endif +} +// each texture at the coordinates shifted by the heights of its own normal map +#define TRACKBLEND_COORD(heights) parallax_coord(heights, f_coord, viewdir, dx, dy) +#else +#define TRACKBLEND_COORD(heights) f_coord +#endif + void main() { #ifdef TRACKBLEND_SPECGLOSS @@ -93,15 +140,19 @@ void main() // the gradients are taken outside the branches the textures are sampled in vec2 dx = dFdx(f_coord); vec2 dy = dFdy(f_coord); +#ifdef TRACKBLEND_PARALLAX + vec3 viewdir = normalize(vec3(0.0) - TangentFragPos); +#endif #ifdef TRACKBLEND_GRAIN vec2 grainpos = f_coord * GRAIN_CELLS; // grains smaller than a pixel would shimmer, there the textures fade into each other instead float footprint = max(length(dx), length(dy)) * GRAIN_CELLS; float grained = 1.0 - smoothstep(0.35, 0.9, footprint); #endif - vec4 tex_color = textureGrad(diffuse, f_coord, dx, dy); + vec2 coord = TRACKBLEND_COORD(normalmap); + vec4 tex_color = textureGrad(diffuse, coord, dx, dy); #ifdef TRACKBLEND_NORMALMAP - vec4 tex_normal = textureGrad(normalmap, f_coord, dx, dy); + vec4 tex_normal = textureGrad(normalmap, coord, dx, dy); #endif if (total > 0.0) { @@ -120,24 +171,27 @@ void main() #endif if (weights.x > 0.0) { - blended += textureGrad(diffuse1, f_coord, dx, dy) * weights.x; + vec2 coord1 = TRACKBLEND_COORD(normalmap1); + blended += textureGrad(diffuse1, coord1, dx, dy) * weights.x; #ifdef TRACKBLEND_NORMALMAP - blendednormal += textureGrad(normalmap1, f_coord, dx, dy) * weights.x; + blendednormal += textureGrad(normalmap1, coord1, dx, dy) * weights.x; #endif } if (weights.y > 0.0) { - blended += textureGrad(diffuse2, f_coord, dx, dy) * weights.y; + vec2 coord2 = TRACKBLEND_COORD(normalmap2); + blended += textureGrad(diffuse2, coord2, dx, dy) * weights.y; #ifdef TRACKBLEND_NORMALMAP - blendednormal += textureGrad(normalmap2, f_coord, dx, dy) * weights.y; + blendednormal += textureGrad(normalmap2, coord2, dx, dy) * weights.y; #endif } #ifndef TRACKBLEND_SPECGLOSS if (weights.z > 0.0) { - blended += textureGrad(diffuse3, f_coord, dx, dy) * weights.z; + vec2 coord3 = TRACKBLEND_COORD(normalmap3); + blended += textureGrad(diffuse3, coord3, dx, dy) * weights.z; #ifdef TRACKBLEND_NORMALMAP - blendednormal += textureGrad(normalmap3, f_coord, dx, dy) * weights.z; + blendednormal += textureGrad(normalmap3, coord3, dx, dy) * weights.z; #endif } #endif @@ -159,7 +213,7 @@ void main() vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * tex_normal.a; #ifdef TRACKBLEND_SPECGLOSS - vec4 tex_specgloss = textureGrad(specgloss, f_coord, dx, dy); + vec4 tex_specgloss = textureGrad(specgloss, coord, dx, dy); float specularity = tex_specgloss.r; glossiness = tex_specgloss.g * abs(param[1].w); metalic = tex_specgloss.b;