diff --git a/README.md b/README.md index 137f0ea..c5a40a3 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ r_dynamicLightIntensity | Make dynamic lights brighter/dimmer. r_dynamicLightScale | Scale the radius of dynamic lights. r_lerpTextureAnimation | Use linear interpolation on texture animation - flames, explosions. r_maxAnisotropy | Enable [anisotropic filtering](https://en.wikipedia.org/wiki/Anisotropic_filtering). +r_textureVariation | Hide obvious texture tiling in a few Q3A maps. r_waterReflections | Show planar water reflections. Only enabled on q3dm2 for now. ### Console Commands diff --git a/code/renderer_bgfx/Main.h b/code/renderer_bgfx/Main.h index a0da0ee..fa401df 100644 --- a/code/renderer_bgfx/Main.h +++ b/code/renderer_bgfx/Main.h @@ -95,18 +95,19 @@ struct GenericShaderProgramVariant { enum { - None = 0, + None = 0, // Fragment - AlphaTest = 1 << 0, - Bloom = 1 << 1, + AlphaTest = 1 << 0, + Bloom = 1 << 1, DynamicLights = 1 << 2, - SoftSprite = 1 << 3, + SoftSprite = 1 << 3, + TextureVariation = 1 << 4, // Vertex - DepthRange = 1 << 4, + DepthRange = 1 << 5, - Num = 1 << 5 + Num = 1 << 6 }; }; diff --git a/code/renderer_bgfx/Main_frame.cpp b/code/renderer_bgfx/Main_frame.cpp index d11e362..af289e7 100644 --- a/code/renderer_bgfx/Main_frame.cpp +++ b/code/renderer_bgfx/Main_frame.cpp @@ -1220,6 +1220,12 @@ static void RenderCamera(const RenderCameraArgs &args) s_main->uniforms->bloom_Write_Scale.set(vec4(stage.bloom ? 1.0f : 0.0f, 0, 0, 0)); } + if (g_cvars.textureVariation.getBool() && stage.textureVariation) + { + shaderVariant |= GenericShaderProgramVariant::TextureVariation; + //bgfx::setTexture(TextureUnit::Noise, s_main->uniforms->noiseSampler.handle, Texture::getNoise()->getHandle()); + } + bgfx::setState(state); if (args.flags & RenderCameraFlags::UseStencilTest) diff --git a/code/renderer_bgfx/Main_init.cpp b/code/renderer_bgfx/Main_init.cpp index 05f17cf..191c266 100644 --- a/code/renderer_bgfx/Main_init.cpp +++ b/code/renderer_bgfx/Main_init.cpp @@ -117,6 +117,7 @@ void ConsoleVariables::initialize() railSegmentLength = interface::Cvar_Get("r_railSegmentLength", "32", ConsoleVariableFlags::Archive); softSprites = interface::Cvar_Get("r_softSprites", "1", ConsoleVariableFlags::Archive); screenshotJpegQuality = interface::Cvar_Get("r_screenshotJpegQuality", "90", ConsoleVariableFlags::Archive); + textureVariation = interface::Cvar_Get("r_textureVariation", "0", ConsoleVariableFlags::Archive); waterReflections = interface::Cvar_Get("r_waterReflections", "0", ConsoleVariableFlags::Archive | ConsoleVariableFlags::Latch); wireframe = interface::Cvar_Get("r_wireframe", "0", ConsoleVariableFlags::Cheat); diff --git a/code/renderer_bgfx/Meta.cpp b/code/renderer_bgfx/Meta.cpp index dbcd922..d5f1d8b 100644 --- a/code/renderer_bgfx/Meta.cpp +++ b/code/renderer_bgfx/Meta.cpp @@ -243,6 +243,22 @@ static const char * s_bloomWhitelist[] = "textures/sfx2/swirl_r*" }; +static const char * s_textureVariationWhitelist[] = +{ + // q3dm7 + "textures/organics/dirt", + "textures/organics/dirt2", + "textures/organics/dirt_trans", + // q3dm16 + "textures/base_wall/metalfloor_wall_14_specular", + // q3dm17 + "textures/base_wall/metalfloor_wall_15", + // q3dm18 + "textures/base_wall/metalfloor_wall_11", + // q3dm19 + "textures/base_floor/metaltechfloor01final" +}; + void Initialize() { s_meta = Meta(); @@ -335,6 +351,20 @@ void OnMaterialCreate(Material *material) s_meta.plasmaExplosionMaterial = material; } + // Enable texture variation. + bool textureVariation = false; + + for (int k = 0; k < BX_COUNTOF(s_textureVariationWhitelist); k++) + { + const char *entry = s_textureVariationWhitelist[k]; + const char *wildcard = strstr(entry, "*"); + + if (!util::Stricmp(material->name, entry) || (wildcard && !util::Stricmpn(material->name, entry, int(wildcard - entry)))) + { + textureVariation = true; + } + } + for (int i = 0; i < Material::maxStages; i++) { MaterialStage &stage = material->stages[i]; @@ -360,6 +390,8 @@ void OnMaterialCreate(Material *material) stage.bloom = true; } } + + stage.textureVariation = textureVariation; } } diff --git a/code/renderer_bgfx/Precompiled.h b/code/renderer_bgfx/Precompiled.h index 52167a8..38480f6 100644 --- a/code/renderer_bgfx/Precompiled.h +++ b/code/renderer_bgfx/Precompiled.h @@ -134,6 +134,7 @@ struct ConsoleVariables ConsoleVariable railSegmentLength; ConsoleVariable screenshotJpegQuality; ConsoleVariable softSprites; + ConsoleVariable textureVariation; ConsoleVariable waterReflections; ConsoleVariable wireframe; @@ -815,6 +816,7 @@ struct MaterialStage MaterialStageType type = MaterialStageType::ColorMap; MaterialLight light = MaterialLight::None; bool bloom = false; + bool textureVariation = false; vec4 normalScale; vec4 specularScale; @@ -1315,6 +1317,7 @@ struct Texture static Texture *get(const char *name); static const Texture *getDefault(); static const Texture *getIdentityLight(); + static const Texture *getNoise(); static const Texture *getWhite(); static Texture *getScratch(size_t index); static void alias(Texture *from, Texture *to); @@ -1343,7 +1346,8 @@ struct TextureUnit Depth = TU_DEPTH, DynamicLightCells = TU_DYNAMIC_LIGHT_CELLS, DynamicLightIndices = TU_DYNAMIC_LIGHT_INDICES, - DynamicLights = TU_DYNAMIC_LIGHTS + DynamicLights = TU_DYNAMIC_LIGHTS, + Noise = TU_NOISE }; }; @@ -1457,6 +1461,7 @@ struct Uniforms Uniform_int textureSampler = "u_TextureSampler"; Uniform_int bloomSampler = "u_BloomSampler"; + Uniform_int noiseSampler = "u_NoiseSampler"; Uniform_int smaaColorSampler = "u_SmaaColorSampler"; Uniform_int smaaEdgesSampler = "u_SmaaEdgesSampler"; Uniform_int smaaAreaSampler = "u_SmaaAreaSampler"; diff --git a/code/renderer_bgfx/Texture.cpp b/code/renderer_bgfx/Texture.cpp index 6362875..9bdaeee 100644 --- a/code/renderer_bgfx/Texture.cpp +++ b/code/renderer_bgfx/Texture.cpp @@ -118,6 +118,10 @@ struct TextureCache uint8_t whiteImageData[defaultImageDataSize]; uint8_t identityLightImageData[defaultImageDataSize]; TextureImpl *defaultTexture, *identityLightTexture, *whiteTexture; + static const int noiseImageSize = 256; + static const uint32_t noiseImageDataSize = noiseImageSize * noiseImageSize * 4; + uint8_t noiseImageData[noiseImageDataSize]; + TextureImpl *noiseTexture; static const size_t nScratchTextures = 32; uint8_t scratchImageData[nScratchTextures][defaultImageDataSize]; std::array scratchTextures; @@ -138,6 +142,17 @@ struct TextureCache defaultTexture = createTexture("*default", CreateImage(defaultImageSize, defaultImageSize, 4, defaultImageData), TextureFlags::Mipmap, bgfx::TextureFormat::RGBA8); + // Noise texture. + for (uint32_t x = 0; x < noiseImageDataSize; x++) + { + auto r = uint8_t(rand() % 255); + auto g = uint8_t(rand() % 255); + auto b = uint8_t(rand() % 255); + noiseImageData[x] = (255 << 24) | (b << 16) | (g << 8) | r; + } + + noiseTexture = createTexture("*noise", CreateImage(noiseImageSize, noiseImageSize, 4, noiseImageData), TextureFlags::None, bgfx::TextureFormat::RGBA8); + // White texture. memset(whiteImageData, 255, defaultImageDataSize); whiteTexture = createTexture("*white", CreateImage(defaultImageSize, defaultImageSize, 4, whiteImageData), 0, bgfx::TextureFormat::RGBA8); @@ -394,6 +409,11 @@ const Texture *Texture::getIdentityLight() return s_textureCache->textureFromImpl(s_textureCache->identityLightTexture); } +const Texture *Texture::getNoise() +{ + return s_textureCache->textureFromImpl(s_textureCache->noiseTexture); +} + const Texture *Texture::getWhite() { return s_textureCache->textureFromImpl(s_textureCache->whiteTexture); diff --git a/premake5.lua b/premake5.lua index 0a2bb25..eb016a2 100644 --- a/premake5.lua +++ b/premake5.lua @@ -179,7 +179,8 @@ newaction { "AlphaTest", "USE_ALPHA_TEST" }, { "Bloom", "USE_BLOOM" }, { "DynamicLights", "USE_DYNAMIC_LIGHTS" }, - { "SoftSprite", "USE_SOFT_SPRITE" } + { "SoftSprite", "USE_SOFT_SPRITE" }, + { "TextureVariation", "USE_TEXTURE_VARIATION" } } local genericVertexVariants = diff --git a/shaders/Generic_fragment.sc b/shaders/Generic_fragment.sc index fae5db5..00b85c6 100644 --- a/shaders/Generic_fragment.sc +++ b/shaders/Generic_fragment.sc @@ -120,6 +120,51 @@ vec3 ClosestPointOnLineSegment(vec3 A, vec3 B, vec3 P) return A + AB * saturate(distance); } +#if defined(USE_TEXTURE_VARIATION) +// https://www.shadertoy.com/view/4tyGWK +// http://www.iquilezles.org/www/articles/texturerepetition/texturerepetition.htm +// Modification by huwb. Original shader by iq: https://www.shadertoy.com/view/lt2GDd +// Created by inigo quilez - iq/2015 +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + +// utilities for randomizing uvs +vec4 hash4( vec2 p ) { return fract(sin(vec4( 1.0+dot(p,vec2(37.0,17.0)), 2.0+dot(p,vec2(11.0,47.0)), 3.0+dot(p,vec2(41.0,29.0)), 4.0+dot(p,vec2(23.0,31.0))))*103.); } +vec2 transformUVs( vec2 iuvCorner, vec2 uv ) +{ + // random in [0,1]^4 + vec4 tx = hash4( iuvCorner ); + // scale component is +/-1 to mirror + tx.zw = sign( tx.zw - 0.5 ); + // random scale and offset + return tx.zw * uv + tx.xy; +} + +// here is a derivative-free version of the 4 samples algorithm from iq +vec4 textureNoTile_4weights(vec2 uv) +{ + // compute per-tile integral and fractional uvs. + // flip uvs for 'odd' tiles to make sure tex samples are coherent + vec2 fuv = mod( uv, 2. ), iuv = uv - fuv; + vec3 BL_one = vec3(0.,0.,1.); // xy = bot left coords, z = 1 + if( fuv.x >= 1. ) fuv.x = 2.-fuv.x, BL_one.x = 2.; + if( fuv.y >= 1. ) fuv.y = 2.-fuv.y, BL_one.y = 2.; + + // smoothstep for fun and to limit blend overlap + vec2 b = smoothstep(0.25,0.75,fuv); + + // fetch and blend + vec4 res = mix( + mix( texture2D( u_DiffuseSampler, transformUVs( iuv + BL_one.xy, uv ) ), + texture2D( u_DiffuseSampler, transformUVs( iuv + BL_one.zy, uv ) ), b.x ), + mix( texture2D( u_DiffuseSampler, transformUVs( iuv + BL_one.xz, uv ) ), + texture2D( u_DiffuseSampler, transformUVs( iuv + BL_one.zz, uv ) ), b.x), + b.y ); + + return res; +} + +#endif + void main() { if (u_PortalClip.x == 1.0) @@ -137,7 +182,11 @@ void main() texCoord0 = gl_FragCoord.xy * u_viewTexel.xy; } +#if defined(USE_TEXTURE_VARIATION) + vec4 diffuse = textureNoTile_4weights(texCoord0); +#else vec4 diffuse = texture2D(u_DiffuseSampler, texCoord0); +#endif if (int(u_Animation_Enabled_Fraction.x) == 1.0) { diff --git a/shaders/SharedDefines.sh b/shaders/SharedDefines.sh index f1307b3..dfc0ff2 100644 --- a/shaders/SharedDefines.sh +++ b/shaders/SharedDefines.sh @@ -64,5 +64,6 @@ #define TU_DYNAMIC_LIGHT_CELLS 4 #define TU_DYNAMIC_LIGHT_INDICES 5 #define TU_DYNAMIC_LIGHTS 6 +#define TU_NOISE 7 #define USE_HALF_LAMBERT \ No newline at end of file