Native OSP Shaders Warband HLSL Shader Exchange

Users who are viewing this thread

Warband HLSL Shader Exchange [OSP]
HLSL is a rough language to learn, especially if you aren't familiar with C-based languages, but with enough patience and willpower it's a really rewarding and fairly underutilized area of modding.

Getting Start with Shaders:
Read First: Swyter's quick-and-dirty Shader Tutorial
Read Second: Scion's Shader Tutorial via Duh
Read if desperate: My "Your First Shader" Tutorial

Important Threads for anyone working on Shaders:
Shader Stuff- HLSL instruction limits: essential reading for any modder working with shaders.

Specific replies of that thread that are of interest:
Swyter's collection of 1.011 Shader Uniforms
xenoargh's guide to optimization on specific shader usage


Collection of links to previously shared HLSL code:

Wanting to find help elsewhere online?
If you are trying to find shader information on the internet, it's best to include hlsl, Dx9 and Shader Model 2 (vs_2, ps_2) in your search terms. There's been a lot of evolution in shader tech since our poor engine was finalized and it's honestly difficult to seek out relevant things. Conveniently Source Engine is a very popular engine that also is limited to the same shader models that we are.
 
Last edited:
LoZ: Breath of The Wild Inspired Toon Shader
6iYUCG5.jpg

With Rim-lighting, Specular, Two-Tone lighting
HLSL translation based on this tutorial online

C-like:
{
    PS_OUTPUT Output;
   
    // Register Texture Maps and Uniforms
    float4 color = tex2D(DiffuseTextureSamplerNoWrap, In.Tex0);
   
    float4 specular;

    if(use_specularmap)
        specular = tex2D(SpecularTextureSampler, In.Tex0);
    else
        specular = float4(1,1,1,1);

    float sun_amount = tex2Dproj(ShadowmapTextureSampler, In.ShadowTexCoord).r;
    float3 normal = normalize(In.WorldNormal);
    float3 viewDir = In.ViewDir;
    float fresnel = 1-(saturate(dot(viewDir, In.WorldNormal)));

    float NdotL = dot(-vSunDir, In.WorldNormal); // Normal dot Lighting Direction
    float3 halfVector = normalize(-vSunDir + viewDir);
    float NdotH = dot(normal, halfVector); // Normal dot Half Vector
   
    float4 ambientColor = vAmbientColor * 3;       
   
    float4 shadow = smoothstep(0.75,0.76, dot(vSunDir, In.WorldNormal)) * ambientColor; // Basically makes a antisun to make a shadow
    float4 isLit = NdotL * sun_amount;

    isLit = isLit.r > 0.5 ? float4(1,1,1,1): float4(0,0,0,1); // If under 50% shade, make it completely shaded


    specular = specular * pow(abs(NdotH) * smoothstep(0.75, 0.78, abs(NdotL)), fMaterialPower);

    float specularIntensitySmooth;

     if(use_specularmap)
        specularIntensitySmooth = smoothstep(0.85, 0.86, specular);
    else
        specularIntensitySmooth = smoothstep(0.985, 0.990, specular);
   
    specular *= specularIntensitySmooth;
   
    fresnel = smoothstep(0.85, 0.87, fresnel);
   
    float4 ambientSun;
    float4 ambientSky;

    if(vSunColor.r > 0.5)
        {
        ambientSun = vSunColor * 0.33;
        ambientSky = vSkyLightColor * 0.33;
        }
    else
        {
        ambientSun = vSunColor;
        ambientSky = vSkyLightColor;
        }
       

    float sunIntensity = smoothstep(0.02, 0.05, NdotL);

    float4 light = saturate(ambientColor + ambientSun + ambientSky + (sunIntensity * isLit * vSunColor));
   
    light.rgb -= shadow * 0.25;

    Output.RGBColor = color * In.Color;
    Output.RGBColor *= light;
    Output.RGBColor += (fresnel * vSunColor * isLit) + (specular * vSunColor * isLit);   
    return Output;
}
 
Last edited:
Hey DarthKiller, check out the Getting started with shaders: header in the OP to find tutorials on how to make basic shaders.

Getting started is by far the hardest part of learning, but once you've overcome the initial hump it is easier.

Here's a quick and nasty tutorial specific to implementing these shared shaders into Warband, but don't expect it to be of any quality:
  1. DOWNLOAD TW SHADER PACKAGE
  2. Follow Swyter's tutorial on properly setting up the necessary system environment.
  3. Now that you can build a new mb.fx, at the end of the source file, set up your pixel shader. You'll need to know what Vertex Shader it'll be using at this point. I made a simple bespoke one because I didn't want lighting data passed through vertex color, but it's your preference.
  4. Third, define your Technique. This is the actual shader name. You'll need to place in both the vertex shader and pixel shader here. This also determines the name you'll want to include in your shader BRF. It will look something like
    1. C-like:
      technique fate_anime_shader
      {
      pass P0
      {
      VertexShader = compile vs_2_0 vs_fate_anime_shader_color();
      PixelShader = compile ps_2_a ps_fate_anime_shader_color();
      }
      }
  5. Compile!
  6. Now, register the shader in a BRF. I do not know what the texture access fields mean so I copy a Native shader with the appropriate maps for this. Use the i.e. if I need Diffuse, Spec, Normal, and Skinning I find the appropriate standart_ shader, copy it, use the standart_ as my fallback and change the shader name to that in my defined Technique.
    1. ofp0OMo.png
    2. In this example the shader only uses a diffuse map, so we only need the singular map.


But, in the nature of sharing, here's a goofy little experiment I conducted:
Screentone / Dot shading (wip)
via Screenspace and Diffuse2 RGB channels

tewCHHx.jpg

Idea: recreate Manga style dot shading techniques using screenspace in the pixel shader with as little effort on my part as possible. This of course will look better the more simple the target model's texture is. I cannot recommend a texture as busy as the example

Problems and Errors: while dividing the z from ScreenSpace does make an effective Screenspace approximation, there are issues a few issues. Since this is determining the screen space of vertices, the fewer vertices a model has, the less accurate it will be close up. Also, on faces that are approaching parallel to the camera's viewpoint the screenspace approximation nearly totally falls apart.

Necessary Setup:
Screentone Texture: I used PixaFlux to assign three black and white screentone images to the R, G and B channels of a texture resulting in this.
ex4H7mG.png
Vertex Shader Setup: You need to pass the Screenspace to the PS via an unused texcoord
C-like:
struct VS_OUTPUT_***
{
    // Add this to the header
    float4 ScreenSpace        : TEXCOORD3; // Any unused TexCoord will work
};

// Add this to the Vertex Shader proper.
Out.ScreenSpace.xy = (float2(Out.Pos.x, -Out.Pos.y) + Out.Pos.w)/2;
    Out.ScreenSpace.xy += (vDepthRT_HalfPixel_ViewportSizeInv.xy * Out.Pos.w);
    Out.ScreenSpace.zw = Out.Pos.zw;

Pixel Shader Snippet
C-like:
PS_OUTPUT ps_***(VS_OUTPUT_*** /*the modified one from earlier*/ In)
{
    PS_OUTPUT Output;

    // Register Texture Maps and Uniforms
    float4 shading = tex2D(Diffuse2Sampler, In.ScreenSpace.xy);
    // This will apply texture coordinates based on the vertex location in screenspace!
 
    /*
        The rest of your pixel shader.
     
     
        At the very bottom after applying the lighting model (which is probably the most wasteful way I could have done this) My reasoning for applying lighting than removing it is I want the texture's perceived luminosity to be what affects the application of the shading, not just the lighting information.
    */
 
    float luminosity = (Output.RGBColor.r * 0.299f + Output.RGBColor.g * 0.587f + Output.RGBColor.b * 0.114f);
    // According to W3C this is how humans perceive brightness. It is better than averaging the colors as it doesn't perceive greys as darker.
 
    Output.RGBColor = color; // I reset the color after finding the luminosity of the pixel
 
    // These, of course, are personal preference and highly dependent on the texture.
    // For mine green was 20% shaded, red was meant to be 50% and blue was supposedly 80%
    // But it didn't look ~right~ to me so I fiddled around
    if (luminosity < 0.15)
        Output.RGBColor.rgb *= (shading.b * shading.g);
 
    if (luminosity < 0.2 && luminosity > 0.15)
        Output.RGBColor.rgb *= shading.r;
 
    if (luminosity < 0.5 && luminosity > 0.2)
        Output.RGBColor.rgb *= shading.g;
 
    // You can now reapply your specular and other effects to get a more natural look.
 
    return Output;
}

Alternate uses:
Don't feel like UVing models? Use screenspace as texture coords like we did for the shading texture.
Particularly like the cartoon Chowder? Boom, Chowder styled textures.
Want a Starwars styled hologram? Use screenspace and some maths and you got yourself a scrolling hologram texture.
EDIT: Updated Screenspace calculation to match TW's native maths. It was better.
 
Last edited:
So I finally convinced myself to try the shaders and managed to copy VC windy flora shaders to base Native file without the stuff I didn't need (seasons and stuff) and it worked. It definitely isn't something I should be proud of but I'm regardless (kinda) :mrgreen:

Now that I have a base for my mod and some ideas, I'm definitely going to fiddle around with it, thanks for convincing me via this thread!
 
A PostFX Modification:
Boost Saturation
(without making everything technicolor)
0uTYg.png

Notice how the whites don't become vomit and more saturate colors saturate at slower rates

So, there are plenty of ways to handle this, such as this tweak that performs a fantastic desaturation effect, but I wanted a slightly different way to handle it, so:
First, add these declarations to the top of the PostFX.fx file
C-like:
// I placed it just under float g_DOF_Range = ***;
// Courtesy of Ian Taylor @ https://www.chilliant.com/rgb2hsv.html
// Y'all know I cannot math like this.

  float3 HUEtoRGB(in float H)
  {
    float R = abs(H * 6 - 3) - 1;
    float G = 2 - abs(H * 6 - 2);
    float B = 2 - abs(H * 6 - 4);
    return saturate(float3(R,G,B));
  }
 
  float Epsilon = 1e-10;

  float3 RGBtoHCV(in float3 RGB)
  {
    // Based on work by Sam Hocevar and Emil Persson
    float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0);
    float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx);
    float C = Q.x - min(Q.w, Q.y);
    float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
    return float3(H, C, Q.x);
  }

float3 RGBtoHSV(in float3 RGB)
  {
    float3 HCV = RGBtoHCV(RGB);
    float S = HCV.y / (HCV.z + Epsilon);
    return float3(HCV.x, S, HCV.z);
  }
 

  float3 HSVtoRGB(in float3 HSV)
  {
    float3 RGB = HUEtoRGB(HSV.x);
    return ((RGB - 1) * HSV.y + 1) * HSV.z;
  }

Second, in the final scene pass, conveniently called FinalScenePassPS add this after the gamma adjustments

C-like:
    float boost = 1.5f;  
    float3 hsv = RGBtoHSV(color.rgb);
    float3 saturated = hsv;
    saturated.y *= boost;
    color.rgb = lerp(HSVtoRGB(saturated), HSVtoRGB(hsv), hsv.y);

So, from my admittedly very basic understanding, this will take a look at the color, determine the Hue, Saturation and Value and boost the saturation of colors that are more washed out, while leaving the already more saturated colors alone. You could change this to boosting darker color's saturation by swapping the lerp value from hsv.y to hsv.z.

boostof 2 looks natural enough, 5 looks nasty, and anything over 10 will be strong enough to make that lerp useless.

This will also add many new arithmetics to the shaders, so you'll need to change all the compile PS_2_0 to PS_2_X to give yourself more instruction slots

Alternate Uses: Simpler saturations by just doing

C-like:
float3 hsv = RGBtoHSV(color.rgb);
hsv.y *= 0.6;
color.rgb = HSVtoRGB(hsv);

// Make the entire screen look like Predator Vision by making the saturation value something like 4
// Or technicolor vomit by boosting it to something wild like 255
 
Last edited:
Enhanced Fog
20201104141146_1.jpg

(with more fog collecting at lower altitudes)​

Pretty straight forward technique, I'm surprised this isn't the default behavior, to be honest.

C-like:
float get_fog_amount_new(float d, float wz)
{
   
    wz = max(wz, 0.05f);    // This limits the minimum altitude, because the strength will increase exponentially as it approaches 0
    float valleys = pow(wz, -1);
   
    return get_fog_amount(4 * d * fFogDensity + (valleys * d * d * fFogDensity));
    // This is more up to artistic intent. I like dark, thick fog, hence the power.
}
There are a few tweaks you could make.
I like the idea of removing the just distance based fog entirely, allowing it to collect solely on scene altitude, with the power determined on distance from camera.
 
Easy-Peasy Lemon Squeezy
Playstationify Vertex Shader
(faux affine texture mapping, vertex snapping)


Warning: Not for those who get motion sickness easily

Register this with the other functions:
C-like:
float4 playstationify(float4 vPos)
{
  
    int vRes = 120;    // Vertical Resolution. Lower = chunkier snapping
    int hRes = 160;    // Horizontal Resolution. Lower = chunkier snapping
  
    float4 projection = mul(matWorldViewProj, vPos); // Get vertex position in world view
  
    float4 vertex = projection;                        // Make a copy
    vertex.xyz = projection.xyz / projection.w;        // flatten it along the depth
    vertex.x = floor(hRes * vertex.x) / hRes;        // This will lose precision of the float, causing the snapping
    vertex.y = floor(vRes * vertex.y) / vRes;
    vertex.xyz *= projection.w;                        // This will then readd the depth
  
    if(vertex.w > 0)            // If in front of the camera . . .
        vertex /= vertex.w;        // Purposefully remove the depth information to break the texture projection in order to cause affine texture mapping
  
    return vertex;
}

Then instead of using Out.Pos = mul(matWorldViewProj, vPosition);
use Out.Pos = playstationify(vPosition);
And you've converted that Vertex Shader into a PSX style monstrosity.

For skinning support first do the deform and then run the deformed vObjectPos through the playstationify magic box.

And for anyone who just wants to see what it's like if you drop this shader into any (noninstanced) native shader
 
Last edited:
Hi, I am new on HLSL, is it possible to perform model frames through HLSL?
I mean in OPENbrf some models with frames like xbox, bow, however their frames can only be performed as scene props or items when on attack/reload. Is it possible to do so as items display its own frames all the time through HLSL?
 
I'm fairly confident in saying no, unfortunately. There's no way for us to instruct a shader to load the next frame of a vertex animation.

The strength of the shader is its speed at runtime, but it sacrifices access to things like memory and outside information to make that possible.
 
I'm fairly confident in saying no, unfortunately. There's no way for us to instruct a shader to load the next frame of a vertex animation.

The strength of the shader is its speed at runtime, but it sacrifices access to things like memory and outside information to make that possible.
Thx for your reply.
 
Enhanced Bloodied Effects
(using Native's Vertex Color Blood)

UZwPz.png
For best results, you can make a blood map per texture, but a generalized one could also work, albeit with potentially weird results
Time to Implement, 10 minutes. Time to fix the odd lightening issue, dunno, I'll update this post if I accomplish a fix.

This takes advantage of RGBtoHSV listed in the Boost Saturation post above.

I popped mine into the ps_main_standart shader, full code of which will be included in the bottom most spoiler, but the gist of it is very simple, set up a normal native material, but in Diffuse2 add in a bloodified version of the diffuse texture. We will interpolate between that and the base texture by taking the diffence between the Vertex Coloring and its base color of white, as it will become more saturated, the more bloodied we find ourselves.

C-like:
        float4 tex2_col = tex2D(Diffuse2Sampler, In.Tex0);        //Register the Blood Texture
        float bloodAmount = distance(In.VertexColor.rgb, float3(1,1,1)); // Further from pure white = larger number is distance
        Output.RGBColor.rgb *= lerp(tex_col.rgb, tex2_col.rgb, bloodAmount * tex2_col.a);    // lerp between the two, based how far from the base white the vertex coloring is multiplied by the blood texture's alpha (to support transparent textures)
        INPUT_TEX_GAMMA(Output.RGBColor.rgb); // this line is all it took to correct the texture lightening issue

Pretty straightforward, huh?

Now, implementing it in Native took some doing, so here's everything I had to tweak, literally copy-pasted from my file:
The pixel shader
C-like:
PS_OUTPUT ps_main_standart ( VS_OUTPUT_STANDART In, uniform const int PcfMode,
                                    uniform const bool use_bumpmap, uniform const bool use_specularfactor,
                                    uniform const bool use_specularmap, uniform const bool ps2x,
                                    uniform const bool use_aniso, uniform const bool terrain_color_ambient = true,
                                    uniform const bool use_bloodmap = false)
{
    PS_OUTPUT Output;

    float3 normal;
    if(use_bumpmap) {
        normal = (2.0f * tex2D(NormalTextureSampler, In.Tex0) - 1.0f);
    }
    else
    {
        normal = In.SunLightDir;
    }


    float sun_amount = 1;
    if (PcfMode != PCF_NONE)
    {
        if((PcfMode == PCF_NVIDIA) || ps2x)        //we have more ins count for shadow, add some ambient factor to sun amount
            sun_amount = 0.05f + GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
        else
            sun_amount = GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
    }

    //define ambient term:
    const int ambientTermType = ( terrain_color_ambient && (ps2x || !use_specularfactor) ) ? 1 : 0;
    const float3 DirToSky = use_bumpmap ? In.SkyLightDir : float3(0.0f, 0.0f, 1.0f);
    float4 total_light = get_ambientTerm(ambientTermType, normal, DirToSky, sun_amount);


    float3 aniso_specular = 0;
    if(use_aniso) {
        if(!ps2x){
            GIVE_ERROR_HERE;
        }
        float3 direction = float3(0,1,0);
        aniso_specular  = calculate_hair_specular(normal, direction, ((use_bumpmap) ?  In.SunLightDir : -vSunDir), In.ViewDir, In.Tex0);
    }

    if( use_bumpmap)
    {
        total_light.rgb += (saturate(dot(In.SunLightDir.xyz, normal.xyz)) + aniso_specular) * sun_amount * vSunColor;

        if(ps2x || !use_specularfactor) {
            total_light += saturate(dot(In.SkyLightDir.xyz, normal.xyz)) * vSkyLightColor;
        }
        #ifdef INCLUDE_VERTEX_LIGHTING
        if(ps2x || !use_specularfactor || (PcfMode == PCF_NONE))
        {
            total_light.rgb += In.VertexLighting;
        }
        #endif

        #ifndef USE_LIGHTING_PASS
            float light_atten = In.PointLightDir.a;
            const int effective_light_index = iLightIndices[0];
            total_light += saturate(dot(In.PointLightDir.xyz, normal.xyz) * vLightDiffuse[effective_light_index]  * light_atten);
        #endif
    }
    else {
        total_light.rgb += (saturate(dot(-vSunDir, normal.xyz)) + aniso_specular) * sun_amount * vSunColor;

        if(ambientTermType != 1 && !ps2x) {
            total_light += saturate(dot(-vSkyLightDir.xyz, normal.xyz)) * vSkyLightColor;
        }
        #ifdef INCLUDE_VERTEX_LIGHTING
        total_light.rgb += In.VertexLighting;
        #endif
    }

    if (PcfMode != PCF_NONE)
        Output.RGBColor.rgb = total_light.rgb;
    else
        Output.RGBColor.rgb = min(total_light.rgb, 2.0f);

    // Output.RGBColor.rgb = total_light.rgb;    //saturate?
    Output.RGBColor.rgb *= vMaterialColor.rgb;

    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0);

    if(use_bloodmap)
    {
        float4 tex2_col = tex2D(Diffuse2Sampler, In.Tex0);
        float bloodAmount = distance(In.VertexColor.rgb, float3(1,1,1));
        bloodAmount *= 0.25f;
        Output.RGBColor.rgb *= lerp(tex_col.rgb, tex2_col.rgb, bloodAmount);
        INPUT_TEX_GAMMA(Output.RGBColor.rgb);
    }
    else {
        Output.RGBColor.rgb *= tex_col.rgb;
        Output.RGBColor.rgb *= In.VertexColor.rgb;
        INPUT_TEX_GAMMA(Output.RGBColor.rgb);
    }



    //add specular terms
    if(use_specularfactor) {
        float4 fSpecular = 0;

        float4 specColor = 0.1 * spec_coef * vSpecularColor;
        if(use_specularmap) {


            //float spec_tex_factor = dot(tex2D(SpecularTextureSampler, In.Tex0).rgb,0.33);
            //get more precision from specularmap
  
            float spec_tex_factor = tex2D(SpecularTextureSampler, In.Tex0).rgb;
  
  
            specColor *= spec_tex_factor;
        }
        else //if(use_specular_alpha)    //is that always true?
        {
            specColor *= tex_col.a;
        }

        float4 sun_specColor = specColor * vSunColor * sun_amount;

        //sun specular
        float3 vHalf = normalize( In.ViewDir + ((use_bumpmap) ?  In.SunLightDir : -vSunDir) );
        fSpecular = sun_specColor * pow( saturate(dot(vHalf, normal)), fMaterialPower);
        if(PcfMode != PCF_DEFAULT)    //we have 64 ins limit
        {
            fSpecular *= In.VertexColor;
        }

        if(use_bumpmap)
        {
            if(PcfMode == PCF_NONE)    //add point lights' specular color for indoors
            {
                fSpecular.rgb += specColor * In.ShadowTexCoord.rgb;    //ShadowTexCoord => point lights specular! (calculate_point_lights_specular)
            }
  
            //add more effects for ps2a version:
            if(ps2x || (PcfMode == PCF_NONE)) {
  
                #ifndef USE_LIGHTING_PASS
                //effective point light specular
                float light_atten = In.PointLightDir.a;
                const int effective_light_index = iLightIndices[0];
                float4 light_specColor = specColor * vLightDiffuse[effective_light_index] * (light_atten * 0.5);     //dec. spec term to remove "effective light change" artifacts
                vHalf = normalize( In.ViewDir + In.PointLightDir );
                fSpecular += light_specColor * pow( saturate(dot(vHalf, normal)), fMaterialPower);
                #endif
            }
        }
        else
        {
            //fSpecular.rgb += specColor * In.SkyLightDir * 0.1;    //SkyLightDir-> holds lights specular color (calculate_point_lights_specular)
            fSpecular.rgb += (specColor + In.SkyLightDir) * 0.1;    //SkyLightDir-> holds lights specular color (calculate_point_lights_specular)
        }
  
        Output.RGBColor += fSpecular;
    }
    else if(use_specularmap) {
        GIVE_ERROR_HERE;
    }

    OUTPUT_GAMMA(Output.RGBColor.rgb);


    //if we dont use alpha channel for specular-> use it for alpha
    Output.RGBColor.a = In.VertexColor.a;    //we dont control bUseMotionBlur to fit in 64 instruction

    if( (!use_specularfactor) || use_specularmap) {
        Output.RGBColor.a *= tex_col.a;
    }

    return Output;
}
The technique macros (Twice! I dunno why but the macros are each registered again immediately after)
C-like:
#define DEFINE_STANDART_TECHNIQUE(tech_name, use_bumpmap, use_skinning, use_specularfactor, use_specularmap, use_aniso, terraincolor, use_bloodmap)    \
                technique tech_name    \
                { pass P0 { VertexShader = standart_vs_noshadow[(2*use_bumpmap) + use_skinning]; \
                            PixelShader = compile ps_2_0 ps_main_standart(PCF_NONE, use_bumpmap, use_specularfactor, use_specularmap, false, use_aniso, terraincolor, use_bloodmap);} } \
                technique tech_name##_SHDW    \
                { pass P0 { VertexShader = standart_vs_default[(2*use_bumpmap) + use_skinning]; \
                            PixelShader = compile ps_2_0 ps_main_standart(PCF_DEFAULT, use_bumpmap, use_specularfactor, use_specularmap, false, use_aniso, terraincolor, use_bloodmap);} } \
                technique tech_name##_SHDWNVIDIA    \
                { pass P0 { VertexShader = standart_vs_nvidia[(2*use_bumpmap) + use_skinning]; \
                            PixelShader = compile ps_2_a ps_main_standart(PCF_NVIDIA, use_bumpmap, use_specularfactor, use_specularmap, true, use_aniso, terraincolor, use_bloodmap);} }  \
                DEFINE_LIGHTING_TECHNIQUE(tech_name, 0, use_bumpmap, use_skinning, use_specularfactor, use_specularmap)

                  
#define DEFINE_STANDART_TECHNIQUE_HIGH(tech_name, use_bumpmap, use_skinning, use_specularfactor, use_specularmap, use_aniso, terraincolor, use_bloodmap)    \
                technique tech_name    \
                { pass P0 { VertexShader = compile vs_2_0 vs_main_standart(PCF_NONE, use_bumpmap, use_skinning); \
                            PixelShader = compile PS_2_X ps_main_standart(PCF_NONE, use_bumpmap, use_specularfactor, use_specularmap, true, use_aniso, terraincolor, use_bloodmap);} } \
                technique tech_name##_SHDW    \
                { pass P0 { VertexShader = compile vs_2_0 vs_main_standart(PCF_DEFAULT, use_bumpmap, use_skinning); \
                            PixelShader = compile PS_2_X ps_main_standart(PCF_DEFAULT, use_bumpmap, use_specularfactor, use_specularmap, true, use_aniso, terraincolor, use_bloodmap);} } \
                technique tech_name##_SHDWNVIDIA    \
                { pass P0 { VertexShader = compile vs_2_0 vs_main_standart(PCF_NVIDIA, use_bumpmap, use_skinning); \
                            PixelShader = compile ps_2_a ps_main_standart(PCF_NVIDIA, use_bumpmap, use_specularfactor, use_specularmap, true, use_aniso, terraincolor, use_bloodmap);} } \
                DEFINE_LIGHTING_TECHNIQUE(tech_name, 0, use_bumpmap, use_skinning, use_specularfactor, use_specularmap)

and add a false to every Native shader registered using those macros
C-like:
DEFINE_STANDART_TECHNIQUE( standart_noskin_bump_nospecmap,                 true, false, true, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_noskin_bump_specmap,                 true, false, true, true,  false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_skin_bump_nospecmap,                 true, true,  true, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_skin_bump_specmap,                     true, true,  true, true,  false, true, false)
              
//high versions:
DEFINE_STANDART_TECHNIQUE_HIGH( standart_skin_bump_nospecmap_high,         true, true,  true, false, false, true, false)
DEFINE_STANDART_TECHNIQUE_HIGH( standart_skin_bump_specmap_high,         true, true,  true, true , false, true, false)
DEFINE_STANDART_TECHNIQUE_HIGH( standart_noskin_bump_nospecmap_high,     true, false,  true, false, false, true, false)
DEFINE_STANDART_TECHNIQUE_HIGH( standart_noskin_bump_specmap_high,         true, false,  true, true , false, true, false)
                                                                            
//-----------------------------------------------
//nobump versions:
DEFINE_STANDART_TECHNIQUE( standart_noskin_nobump_nospecmap,             false, false, true, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_noskin_nobump_specmap,                 false, false, true, true , false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_skin_nobump_nospecmap,                 false,  true, true, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_skin_nobump_specmap,                 false,  true, true, true , false, true, false)
                                                                              
//-----------------------------------------------
//nospec versions:
//
DEFINE_STANDART_TECHNIQUE( standart_noskin_nobump_nospec,                 false, false, false, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_noskin_bump_nospec,                 true,  false, false, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_noskin_bump_nospec_noterraincolor,     true,  false, false, false, false, false, false)
DEFINE_STANDART_TECHNIQUE( standart_skin_nobump_nospec,                 false,  true, false, false, false, true, false)
DEFINE_STANDART_TECHNIQUE( standart_skin_bump_nospec,                     true,   true, false, false, false, true, false)
                                                                              
//nospec_high
DEFINE_STANDART_TECHNIQUE_HIGH( standart_noskin_bump_nospec_high,                 true, false, false, false, false, true, false)
DEFINE_STANDART_TECHNIQUE_HIGH( standart_noskin_bump_nospec_high_noterraincolor, true, false, false, false, false, false, false)
DEFINE_STANDART_TECHNIQUE_HIGH( standart_skin_bump_nospec_high,                 true,  true, false, false, false, true, false)

All that, just to add:
DEFINE_STANDART_TECHNIQUE_HIGH( standart_skin_bump_specmap_high_blood, true, true, true, true , false, true, true)

Possible Tweaks:
Change the blood texture to blood splatter over an alpha instead of over the base texture and apply that to the texture based on bloodiedness levels to allow it to be reused in many places. (this will also allow you to use the alpha as a specular to make the blood shinier than surrounding areas)
Instead of using the saturation value of the vColor, use float blood_amount = distance(vColor.rgb, float3(1,1,1); as it should become a larger value as you become more bloodied. Needs testing to see if it would interpolate enough into the bloodied texture.

Oh, and here's my test texture, if you are doing a bespoke blood texture per model, I'd recommend adding splatter the the arms, neck, and anywhere else you'll commonly hit/be hit.
9wdag.png

There were some conversations about some potential uses inside the #wb-3d-art channel of the discord, which are copied below
dstn
Well, since the engine applies blood via vertex coloring there's not a whole lot you can do without a lot of bodging. Using RBGtoHSV on the In.VertexColor and you can take the .x (hue) channel of the HSV'd color to determine if the hue is red, and then check the .y (saturation) channel to determine if it's over a certain level of saturation, and then if all that passes, use the .y (saturation) inside the lerp function.
That just really ramps up the instructions count. And won't work at all if the mesh is already using red vertex colors.


Burspa
oh yea that's a good idea. I wonder how many meshes actually have vertex colours, might be a non problem. The addition of blood maps for 2 many meshes seems like it could mount up in resource costs. But I guess special ones would work. You could just multiply Output.RGBColor.rgb *= The blood colour you want; Instead of in.vertexcolor to tint blood to different colours right?

dstn
That could work if you used the bloodAmount to mask it so you wouldn't accidentally tint the entire texture. You could also do something like a generic blood texture with transparency for all armors, and do a different one per race, or do it in greyscale and tint it in the shader per race. But in that case you would have to probably multiply the bloodAmount inside the lerp by the alpha channel of the blood map so it wouldn't apply where it shouldn't.
 
Last edited:
I thought I made a post of this particular postFX filter, but apparently I only shared the glory on discord. Here's:
Palette Swap

(low-fi graphics to chill/kill looters to)
Keep in mind I made 5 months ago and so I am not 100% sure what that Dustin was thinking, but I know him better than most and think I can translate my own nonsense to another useless post.


Concept: Using the final PostFx pass, take the colors in a scene and convert it to a more limited palette to emulate older hardware palettes. (I use https://lospec.com/ to get the hexadecimals of the palettes, and then convert that to a percentage RGB later)

I also use noise lifted from https://www.shadertoy.com/view/4t23RW in order to break up large blocks of single colors since I was using 4 color CGA palettes. I am not clever enough to make it an actual dither, but the generative noise works well enough as is.

C:
//inside the definitions at the top
#define Palette             (postfxParams3.w)

// just before the close of FinalScenePassPS, before return color;
if (Palette != 1.0f) {
    float pixelDepth = tex2D(postFX_sampler4, texCoord).r; //Conditions of their variables definitions aren't always true and so it needs to be redefined here.
    float3 hsv = RGBtoHSV(color.rgb);

    float N = pow(2.0, 6.0 - 5.0 * hsv.z - pixelDepth);
    float seed = random(hsv.y); // hsv.y is Saturation

    float3 noise = floor(N * color.rgb + 2.0 * seed) / N;

    color.rgb = lerp(color.rgb, noise, 0.5); // Liner Interpolation to mix the scene's colors with the generative noise

    color.rgb = floor(color.rgb * 16) / 16; // This line is interesting, essentially we are removing accuracy from the colors.
// My assumption here is that by removing the amount of renderable colors to 4096, it will look more ~retro~, you can drop it even further down
// by dropping that 16 to a smaller number. I think #^3 should be a count for the colors remaining in the palette.
// you can use this multiply than floor technique to make low resolution shaders, as seen in the playstationify shader above.

    hsv = RGBtoHSV(color.rgb);

    int paletteChoice = Palette;
    // I am using the postfxParams3.w as a way to declare palette inside of module_postfx.py since it doesn't seem there is another way to pass info to the post shaders on the fly
    // I can't seem to see where any of the floats in postfxParams3 get used anywhere, so you might see me using these things as inputs in later postFX

    float3 palette[4]; // I am declaring the number of floats inside the list it might expand automatically when you put more than expected, but since all my examples only have 4, I used 4

    if(paletteChoice == 0){
    palette[0] = float3(0.33,1,0.33);
    palette[1] = float3(1,0.33,0.33);
    palette[2] = float3(1,1,0.33);
    palette[3] = float3(0,0,0);}

    else if(paletteChoice == 1){
    palette[0] = float3(0.33,1,1);
    palette[1] = float3(1,0.33,1);
    palette[2] = float3(1,1,1);
    palette[3] = float3(0,0,0);}

    else if(paletteChoice == 2){
    palette[0] = float3(0.33,1,1);
    palette[1] = float3(1,0.33,0.33);
    palette[2] = float3(1,1,1);
    palette[3] = float3(0,0,0);}

    else {
    palette[0] = float3(0.33,1,1);
    palette[1] = float3(1,0.33,1);
    palette[2] = float3(1,1,1);
    palette[3] = float3(0,0,0);}
  
    float3 replacementcolor = float3(0, 0, 0);
    float dist = 1000000.0;

    for (int i = 0; i < 2; i++){
        //I never check the palette[3] because it's already the default replacement color
        // I honestly never thought about this until writing this post. I could rewrite this
        // buuuutttt. . . I'm not gonna
        float3 c = palette[i];
        float d = distance(color.rgb, c);
  
        if (d < dist) {
            dist = d;
            replacementcolor = c;
        }
  
    }

    /*
        I'm sure you can read the above, but basically
        it looks at the new noisy pixel color and compares
        it to all the colors in the palette and whichever color
        in the palette is nearest, it gets assigned
    */

    color.rgb = replacementcolor;

    if(hsv.z > 0.75)
        color.rgb = palette[2];
    else if (hsv.z < 0.15)
        color.rgb = palette[3];
    }

    /*
    I then use the above to replace the brightest and darkest values with their respective
    colors in my palettes, this just adds a much better look, it's fine to skip with a
    larger palette, though
    */


return color;

I'll go through and update this after transcribing a 16 color palette and maybe a 32 color palette just to see, I don't have time at this exact moment to do that atm.

Notes:
  • if (Palette != 1.0f) is used for in scene palette changes, you don't need it. You can alter this pretty easily to avoid using that.
  • This version requires RGBtoHSV, listed in a post above.
 
Last edited:
Earendil had the nice idea to switch between loading screens.
dstn linked me to that one: https://www.shadertoy.com/view/tsfXzN

perfect for the first loading screen or if you want something like a tv screen.


Python:
PS_OUTPUT ps_tc_screen(VS_OUTPUT_FONT_X In)
{
    PS_OUTPUT Output;

    float offsetX = 1.25;
    float offsetY = 4.5;
    float slideTime = time_var * 0.3;

    float mix1 = clamp(exp(2.0 * sin(slideTime)) - offsetY, 0.0 , 1.0);

    float mix2 = clamp(exp(2.0 * sin(slideTime - offsetX)) - offsetY, 0.0 , 1.0);

    float mix3 = clamp(exp(2.0 * sin(slideTime - offsetX * 2.0)) - offsetY, 0.0 , 1.0);

    float mix4 = clamp(exp(2.0 * sin(slideTime - offsetX* 3.0)) - offsetY, 0.0 , 1.0);

    float mix5 = clamp(exp(2.0 * sin(slideTime - offsetX* 4.0)) - offsetY, 0.0 , 1.0);

    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0)* mix1;
    float4 tex_col2 = tex2D(Diffuse2Sampler, In.Tex0)* mix2;
    float4 tex_col3 = tex2D(NormalTextureSampler, In.Tex0)* mix3;
    float4 tex_col4 = tex2D(EnvTextureSampler, In.Tex0)* mix4;
    float4 tex_col5 = tex2D(SpecularTextureSampler, In.Tex0)* mix5;

    tex_col = tex_col + tex_col2 + tex_col3 + tex_col4 + tex_col5;
    INPUT_TEX_GAMMA(tex_col.rgb);
    Output.RGBColor =  In.Color * tex_col;
    OUTPUT_GAMMA(Output.RGBColor.rgb);
    return Output;
}

technique tc_screen //Uses gamma
{
    pass P0
    {
        VertexShader = vs_font_compiled_2_0;
        PixelShader = compile ps_2_0 ps_tc_screen();
    }
}
the shader will switch between the different texture samplers

edit: if you want the shader for the starting screen add it to core_shaders
best copy simple_shader and rename the shader and technique.

ougRh.jpg
lVopn.jpg
 
Last edited:
Interlinking the Ghost and Shaders tutorial of @burspa here:


mb.fx file

Code:
///////////////////////////////////////////////////////////////////
//Burspa's Ghost Shader
////////////////


struct VS_OUTPUT_GHOST_SHADER
{
    float4 Pos                    : POSITION;
    float4 Color                    : COLOR0;
    float2 Tex0                    : TEXCOORD0;
    float  Fog                    : FOG;
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;   
};



VS_OUTPUT_GHOST_SHADER vs_ghost(float4 vPosition : POSITION, float3 vNormal : NORMAL, float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{

    INITIALIZE_OUTPUT(VS_OUTPUT_GHOST_SHADER, Out);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPosition);
    
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 P = mul(matWorldView, vPosition); //position in view space

    Out.Tex0 = tc;

    float4 diffuse_light = vAmbientColor + vLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSunDir)) * vSunColor;
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //apply fog
    float d = length(P);
    Out.ViewDir =normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;   

    Out.Fog = get_fog_amount_new(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_ghost(VS_OUTPUT_GHOST_SHADER In)
{
float3 GlowColor = {73.0f,95.0f,92.0f};
float GlowExpon = 3.0f;
    PS_OUTPUT Output;
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0);
    INPUT_TEX_GAMMA(tex_col.rgb);
    Output.RGBColor =  In.Color * tex_col;

//add fresnel term
    
        float fresnel = 1.0-(dot( normalize(In.ViewDir), normalize(In.WorldNormal)));

        fresnel = pow(fresnel,GlowExpon);
        //fresnel = fresnel* GlowColor.rgb;
         float3 result = fresnel * GlowColor.rgb;
        Output.RGBColor.rgb *= result;
        


    OUTPUT_GAMMA(Output.RGBColor.rgb);
    return Output;
}

technique ghost_shader
{
    pass P0
    {
        VertexShader = compile vs_2_0 vs_ghost();
        PixelShader = compile ps_2_0 ps_ghost();
    }
}



struct VS_OUTPUT_FORCEFIELD_SHADER
{
    float4 Pos                    : POSITION;
    float4 Color                    : COLOR0;
    float2 Tex0                    : TEXCOORD0;
    float  Fog                    : FOG;
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;   
};



VS_OUTPUT_FORCEFIELD_SHADER vs_forcefield(float4 vPosition : POSITION, float3 vNormal : NORMAL, float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{

    INITIALIZE_OUTPUT(VS_OUTPUT_FORCEFIELD_SHADER, Out);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPosition);
    
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 P = mul(matWorldView, vPosition); //position in view space

    float _speedX = 0.0f;
    float _speedY = 0.2f;

    tc.xy += frac(time_var * float2(_speedX, _speedY));
    Out.Tex0 = tc;

    float4 diffuse_light = vAmbientColor + vLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSunDir)) * vSunColor;
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //apply fog
    float d = length(P);
    Out.ViewDir =normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;   

    Out.Fog = get_fog_amount_new(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_forcefield(VS_OUTPUT_FORCEFIELD_SHADER In)
{
float3 GlowColor = {0.0f,53.0f,33.0f};
float GlowExpon = 4.0f;
    PS_OUTPUT Output;
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0);
    INPUT_TEX_GAMMA(tex_col.rgb);
    Output.RGBColor =  In.Color * tex_col;

//add fresnel term
    
        float fresnel = 1.0-(dot( normalize(In.ViewDir), normalize(In.WorldNormal)));

        fresnel = pow(fresnel,GlowExpon);
        //fresnel = fresnel* GlowColor.rgb;
         float3 result = fresnel * GlowColor.rgb;
        Output.RGBColor.rgb *= result;
        


    OUTPUT_GAMMA(Output.RGBColor.rgb);
    return Output;
}

technique forcefield_shader
{
    pass P0
    {
        VertexShader = compile vs_2_0 vs_forcefield();
        PixelShader = compile ps_2_0 ps_forcefield();
    }
}


struct VS_OUTPUT_MW
{
    float4 Pos                    : POSITION;
    float4 Color                    : COLOR0;
    float2 Tex0                    : TEXCOORD0;
    float  Fog                    : FOG;
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;   
};



VS_OUTPUT_MW vs_mw(float4 vPosition : POSITION, float3 vNormal : NORMAL, float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{

    INITIALIZE_OUTPUT(VS_OUTPUT_MW, Out);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPosition);
    
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 P = mul(matWorldView, vPosition); //position in view space

    float _speedX = 0.3f;
    float _speedY = 0.1f;

    tc.xy += frac(time_var * float2(_speedX, _speedY));
    Out.Tex0 = tc;

    float4 diffuse_light = vAmbientColor + vLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSunDir)) * vSunColor;
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //apply fog
    float d = length(P);
    Out.ViewDir =normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;   

    Out.Fog = get_fog_amount_new(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_mw(VS_OUTPUT_MW In)
{
float3 GlowColor = {30.0f,30.0f,30.0f};
float GlowExpon = 3.0f;
    PS_OUTPUT Output;
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0);
    INPUT_TEX_GAMMA(tex_col.rgb);
    Output.RGBColor =  In.Color * tex_col;

//add fresnel term
    
        float fresnel = 1.0-(dot( normalize(In.ViewDir), normalize(In.WorldNormal)));

        fresnel = pow(fresnel,GlowExpon);
        //fresnel = fresnel* GlowColor.rgb;
         float3 result = fresnel * GlowColor.rgb;
        Output.RGBColor.rgb *= result;
        


    OUTPUT_GAMMA(Output.RGBColor.rgb);
    return Output;
}

technique mw_shader
{
    pass P0
    {
        VertexShader = compile vs_2_0 vs_mw();
        PixelShader = compile ps_2_0 ps_mw();
    }
}


struct VS_OUTPUT_MWP
{
    float4 Pos                    : POSITION;
    float4 Color                    : COLOR0;
    float2 Tex0                    : TEXCOORD0;
    float  Fog                    : FOG;
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;   
};



VS_OUTPUT_MWP vs_mwp(float4 vPosition : POSITION, float3 vNormal : NORMAL, float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{

    INITIALIZE_OUTPUT(VS_OUTPUT_MWP, Out);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPosition);
    
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 P = mul(matWorldView, vPosition); //position in view space

    float _speedX = 0.3f;
    float _speedY = 0.1f;

    tc.xy += frac(time_var * float2(_speedX, _speedY));
    Out.Tex0 = tc;

    float4 diffuse_light = vAmbientColor + vLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
    diffuse_light += saturate(dot(vWorldN, -vSunDir)) * vSunColor;
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //apply fog
    float d = length(P);
    Out.ViewDir =normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;   

    Out.Fog = get_fog_amount_new(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_mwp(VS_OUTPUT_MWP In)
{
float3 GlowColor = {29.0f,7.0f,71.0f};
float GlowExpon = 4.0f;
    PS_OUTPUT Output;
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0);
    INPUT_TEX_GAMMA(tex_col.rgb);
    Output.RGBColor =  In.Color * tex_col;

//add fresnel term
    
        float fresnel = 1.0-(dot( normalize(In.ViewDir), normalize(In.WorldNormal)));

        fresnel = pow(fresnel,GlowExpon);
        //fresnel = fresnel* GlowColor.rgb;
         float3 result = fresnel * GlowColor.rgb;
        Output.RGBColor.rgb *= result;
        


    OUTPUT_GAMMA(Output.RGBColor.rgb);
    return Output;
}

technique mwp_shader
{
    pass P0
    {
        VertexShader = compile vs_2_0 vs_mwp();
        PixelShader = compile ps_2_0 ps_mwp();
    }
}
/////////////////////
////Burspa Ghost Shader End
///////////////////////////////////////
Not sure about all his other shaders in that file and what they are doing exactly.
 
Flatten Map from Camera Height
(Procedural Paper Map via Shaders)
NLyAu.gif

Video of the very first implementation (Before I made a trick for the shadowmaps)
This one was sparked by a conversation in the discord started by our dear Moderator.

C-like:
//dstn Flat Map Experiment

float map_smoothstep(float smoothValue, float offset_min = 0, float offset_max = 0)
{
 
    float map_smooth = smoothstep(15 + offset_min, 40 + offset_max, smoothValue);
 
    /* So that I don't have to hunt around and replaces
    the smoothstep values in all the eight or so entires
    I declare this function that does nothing but a fixed
    variant of smoothstep. You can declare offsets for
    min and max so you can blend textures or normals at
    a different pace than you might use for flattening or the like
    You may also use any min or max that you like, I chose those
    at totally random, to be honest.            */
 
    return map_smooth;
}

float map_flatten(float vCameraHeight, float vCurrentHeight)
{
    float flatten, flattened_height;
    flatten = map_smoothstep(vCameraHeight);
    flattened_height = lerp(vCurrentHeight, 0, flatten);
 
    /* This one is frankly, super simple. Based on
    the camera's altitude, lerp between the vertices'
    height and an arbitrary height. I like 0.        */
 
    return flattened_height;
}

You can, of course, do this without new functions, in fact, the video linked at top is just using a series of this snippet in all the different map vertex shaders.
C-like:
float flatten = smoothstep(25, 30, vCameraPos.z);
vPosition.z = lerp(vPosition.z, 0, flatten);

So, I picked up a new trick from the mountain shader that we are going to use to great effect in the pixel shaders. They were passing along the vertex height in world space inside the texture coordinates unused channels! So, we can sneak extra data to the pixel shader level with this!

We need to modify the output structure such that the texture coordinates are a float structure larger than they are currently
so the standard float2 Tex0 : TEXCOORD0; becomes float3 Tex0 : TEXCOORD0; (max is float4).

We'll use this new channel to hold our lerp value for texture transitions.

Alternatively, you could pass along the height lerp value as a vertex color channel like I was initially for an easier time, but this is a lot more accurate.

Here's an example from my experiment using VS_OUTPUT_MAP_BUMP
C-like:
struct VS_OUTPUT_MAP_BUMP
{
    float4 Pos                    : POSITION;
    float4 Color                : COLOR0;
    float3 Tex0                    : TEXCOORD0;
    //float4 SunLight                : TEXCOORD1;
    float4 ShadowTexCoord        : TEXCOORD2;
    float2 ShadowTexelPos        : TEXCOORD3;
    float  Fog                    : FOG;
 
    float3 SunLightDir            : TEXCOORD4;
    float3 SkyLightDir            : TEXCOORD5;
 
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;
};
VS_OUTPUT_MAP_BUMP vs_main_map_bump(uniform const int PcfMode, float4 vPosition : POSITION,
                                    float3 vNormal : NORMAL, float3 vTangent : TANGENT, float3 vBinormal : BINORMAL,
                                    float2 tc : TEXCOORD0, float4 vColor : COLOR0,float4 vLightColor : COLOR1)
{
    INITIALIZE_OUTPUT(VS_OUTPUT_MAP_BUMP, Out);

    //dstn Flat Map Experiment
    float4 vPositionOrig = vPosition;
    vPosition.z = map_flatten(vCameraPos.z, vPosition.z);
    Out.Tex0.z = map_smoothstep(vCameraPos.z);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPositionOrig);
 
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 vWorld_binormal = normalize(mul((float3x3)matWorld, vBinormal)); //normal in world space
    float3 vWorld_tangent  = normalize(mul((float3x3)matWorld, vTangent)); //normal in world space
    float3x3 TBNMatrix = float3x3(vWorld_tangent, vWorld_binormal, vWorldN);
 
    Out.Tex0.xy = tc;

    float4 diffuse_light = vAmbientColor;

    if (true /*_UseSecondLight*/)
    {
        diffuse_light += vLightColor;
    }

    //directional lights, compute diffuse color
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
 
    //point lights
    #ifndef USE_LIGHTING_PASS
    diffuse_light += calculate_point_lights_diffuse(vWorldPos, vWorldN, false, false);
    #endif
 
    //apply material color
    Out.Color = (vMaterialColor * vColor * diffuse_light);
    Out.Color.a = vColor.a;

    //shadow mapping variables

    //move sun light to pixel shader
    Out.SunLightDir = normalize(mul(TBNMatrix, -vSunDir));
 
    if (PcfMode != PCF_NONE)
    {
        float4 ShadowPos = mul(matSunViewProj, vWorldPos);
        Out.ShadowTexCoord = ShadowPos;
        Out.ShadowTexCoord.z /= ShadowPos.w;
        Out.ShadowTexCoord.w = 1.0f;
        Out.ShadowTexelPos = Out.ShadowTexCoord * fShadowMapSize;
        //shadow mapping variables end
    }
 
    Out.ViewDir = normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;
 
    float height = 1 / (Out.Pos.z / 10);
 
    //apply fog
    float3 P = mul(matWorldView, vPosition); //position in view space
    float d = length(P);

    Out.Fog = get_fog_amount_map(d, vWorldPos.z);
    return Out;
}

You might notice a line I haven't mentioned yet, float4 vPositionOrig = vPosition;.
This is how I beat the shadowmap problem. Initially I was flattening the vertices on the z and then passing it along as is normal, but since the world position is changed from when the shadowmap was rendered, I had to come up with a solution to have the world position still appear the same, so I duplicated the vertex position and gave that to float4 vWorldPos = (float4)mul(matWorld,vPositionOrig); to have the shadows map as if the world was still raised.

So now, we have a flat map. Now we need to interpolate the texture to something else inside the PixelShader to change the appearance of the material to that of paper. This also requires us to load a second texture in the material, shift to that texture in the shader, and lerp the normals (coincidentally also the name of my guild).

sarG7.png

stucco_6 looks enough like parchment and I didn't expect myself to get so ingrossed into this to actually want to put effort. (Spoiler, I enjoyed it a lot more than expected and totally should have found an appropriately aged paper texture)

This one has a lot more changes than the vertex shader, but can boil down to a simple list:
  • Register the texture using tex2D(Diffuse2Sampler, In.Tex0.xy);
  • Lerp it using the new In.Tex0.z (or In.Tex0.w in the Mountains shader)
  • Use the same lerp value to change from the normal map to a normal of float3(1,1,1)
  • Strip the lighting using the lerp value to make look more like a map
Example:
C-like:
PS_OUTPUT ps_main_map_bump(VS_OUTPUT_MAP_BUMP In, uniform const int PcfMode)
{
    PS_OUTPUT Output;
 
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0.xy);
    float4 blend_col = tex2D(Diffuse2Sampler, In.Tex0.xy);
    tex_col = lerp(tex_col, blend_col, In.Tex0.z);
    INPUT_TEX_GAMMA(tex_col.rgb);
 
    float3 normal = lerp((2.0f * tex2D(NormalTextureSampler, In.Tex0.xy * map_normal_detail_factor).rgb - 1.0f), 1.0f, In.Tex0.z);
 
    float4 In_SunLight = saturate(dot(normal, In.SunLightDir)) * vSunColor * vMaterialColor;// * vColor;  vertex color needed??
 
    float sun_amount = 1;
    if ((PcfMode != PCF_NONE))
    {
        sun_amount = GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
    }
 
    Output.RGBColor =  lerp(tex_col * ((In.Color + In_SunLight * sun_amount)), tex_col, In.Tex0.z);
 
    //add fresnel term
    {
        float fresnel = 1-(saturate(dot( normalize(In.ViewDir), normalize(In.WorldNormal))));
        fresnel *= fresnel;
        Output.RGBColor.rgb *= max(0.6,fresnel+0.1);
    }
    // gamma correct
    OUTPUT_GAMMA(Output.RGBColor.rgb);
 
    return Output;
}

Now I think this is pretty neat, and I would love to see it used in conjunction with the static placename icons that fade in when the camera gets to a certain height, like you see in some mods. It would be a pretty striking effect for mods that want that parchment map vibe. Also, If you wanted to hide the place icons, like I did originally, you can flatten the map to a higher location, with 8.5f being high enough to cover all icons and names on the Native map.
mapZoomout.gif

Using pic_mb_warrior_2 as the secondary texture and vColor.r as my lerp value
ezgif-3-15791e434b43.gif

Still using vColor.r as the lerp value, but the setting it to 1 before applying it to the color. Also added it to the mountains
ezgif-3-a7f2493dbf90.gif

Finally using Tex0.z(w)! Keeping the mountain shader's vertex colors and shading looked so cool, tbh
mapZoom2dtoPaper.gif

If you aren't very hlsl inclined, below is all the native map shaders and my changes to them.
Warning, this is a direct copy of mine, there may be garbage comments or differences from Native (outside the expected examples from this shader tutorial, obviously)
C-like:
#ifdef MAP_SHADERS

//---
struct VS_OUTPUT_MAP
{
    float4 Pos                    : POSITION;
    float4 Color                : COLOR0;
    float2 Tex0                    : TEXCOORD0;
    float4 SunLight                : TEXCOORD1;
    float4 ShadowTexCoord        : TEXCOORD2;
    float2 ShadowTexelPos        : TEXCOORD3;
    float  Fog                    : FOG;
 
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;
};
VS_OUTPUT_MAP vs_main_map(uniform const int PcfMode, float4 vPosition : POSITION, float3 vNormal : NORMAL,
                            float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{
    INITIALIZE_OUTPUT(VS_OUTPUT_MAP, Out);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPosition);
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space


    Out.Tex0 = tc;

    float4 diffuse_light = vAmbientColor;

    if (true /*_UseSecondLight*/)
    {
        diffuse_light += vLightColor;
    }

    //directional lights, compute diffuse color
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
 
    //apply material color
    //    Out.Color = min(1, vMaterialColor * vColor * diffuse_light);
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //shadow mapping variables
    float wNdotSun = saturate(dot(vWorldN, -vSunDir));
    Out.SunLight = (wNdotSun) * vSunColor * vMaterialColor * vColor;
    if (PcfMode != PCF_NONE)
    {
        float4 ShadowPos = mul(matSunViewProj, vWorldPos);
        Out.ShadowTexCoord = ShadowPos;
        Out.ShadowTexCoord.z /= ShadowPos.w;
        Out.ShadowTexCoord.w = 1.0f;
        Out.ShadowTexelPos = Out.ShadowTexCoord * fShadowMapSize;
        //shadow mapping variables end
    }
 
    Out.ViewDir = normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;
 
    //apply fog
    float3 P = mul(matWorldView, vPosition); //position in view space
    float d = length(P);

    Out.Fog = get_fog_amount_map(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_main_map(VS_OUTPUT_MAP In, uniform const int PcfMode)
{
    PS_OUTPUT Output;
 
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0);
    INPUT_TEX_GAMMA(tex_col.rgb);
 
    float sun_amount = 1;
    if ((PcfMode != PCF_NONE))
    {
        sun_amount = GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
    }
    Output.RGBColor =  tex_col * ((In.Color + In.SunLight * sun_amount));
 
 
    //add fresnel term
    {
        float fresnel = 1-(saturate(dot( normalize(In.ViewDir), normalize(In.WorldNormal))));
        fresnel *= fresnel;
        Output.RGBColor.rgb *= max(0.6,fresnel+0.1);
    }
    // gamma correct
    OUTPUT_GAMMA(Output.RGBColor.rgb);
 
    return Output;
}

DEFINE_TECHNIQUES(diffuse_map, vs_main_map, ps_main_map)    //diffuse shader with fresnel effect

//---
struct VS_OUTPUT_MAP_BUMP
{
    float4 Pos                    : POSITION;
    float4 Color                : COLOR0;
    float3 Tex0                    : TEXCOORD0;
    //float4 SunLight                : TEXCOORD1;
    float4 ShadowTexCoord        : TEXCOORD2;
    float2 ShadowTexelPos        : TEXCOORD3;
    float  Fog                    : FOG;
 
    float3 SunLightDir            : TEXCOORD4;
    float3 SkyLightDir            : TEXCOORD5;
 
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;
};
VS_OUTPUT_MAP_BUMP vs_main_map_bump(uniform const int PcfMode, float4 vPosition : POSITION,
                                    float3 vNormal : NORMAL, float3 vTangent : TANGENT, float3 vBinormal : BINORMAL,
                                    float2 tc : TEXCOORD0, float4 vColor : COLOR0,float4 vLightColor : COLOR1)
{
    INITIALIZE_OUTPUT(VS_OUTPUT_MAP_BUMP, Out);

    //dstn Flat Map Experiment
    float4 vPositionOrig = vPosition;
    vPosition.z = map_flatten(vCameraPos.z, vPosition.z);
    Out.Tex0.z = map_smoothstep(vCameraPos.z);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPositionOrig);
 
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 vWorld_binormal = normalize(mul((float3x3)matWorld, vBinormal)); //normal in world space
    float3 vWorld_tangent  = normalize(mul((float3x3)matWorld, vTangent)); //normal in world space
    float3x3 TBNMatrix = float3x3(vWorld_tangent, vWorld_binormal, vWorldN);
 
    Out.Tex0.xy = tc;

    float4 diffuse_light = vAmbientColor;

    if (true /*_UseSecondLight*/)
    {
        diffuse_light += vLightColor;
    }

    //directional lights, compute diffuse color
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;
 
    //point lights
    #ifndef USE_LIGHTING_PASS
    diffuse_light += calculate_point_lights_diffuse(vWorldPos, vWorldN, false, false);
    #endif
 
    //apply material color
    //    Out.Color = min(1, vMaterialColor * vColor * diffuse_light);
    Out.Color = (vMaterialColor * vColor * diffuse_light);
    Out.Color.a = vColor.a;

    //shadow mapping variables

    //move sun light to pixel shader
    //float wNdotSun = saturate(dot(vWorldN, -vSunDir));
    //Out.SunLight = (wNdotSun) * vSunColor * vMaterialColor * vColor;
    Out.SunLightDir = normalize(mul(TBNMatrix, -vSunDir));
 
    if (PcfMode != PCF_NONE)
    {
        float4 ShadowPos = mul(matSunViewProj, vWorldPos);
        Out.ShadowTexCoord = ShadowPos;
        Out.ShadowTexCoord.z /= ShadowPos.w;
        Out.ShadowTexCoord.w = 1.0f;
        Out.ShadowTexelPos = Out.ShadowTexCoord * fShadowMapSize;
        //shadow mapping variables end
    }
 
    Out.ViewDir = normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;
 
    float height = 1 / (Out.Pos.z / 10);
 
    //apply fog
    float3 P = mul(matWorldView, vPosition); //position in view space
    float d = length(P);

    Out.Fog = get_fog_amount_map(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_main_map_bump(VS_OUTPUT_MAP_BUMP In, uniform const int PcfMode)
{
    PS_OUTPUT Output;
 
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0.xy);
    float4 blend_col = tex2D(Diffuse2Sampler, In.Tex0.xy);
    tex_col = lerp(tex_col, blend_col, In.Tex0.z);
    INPUT_TEX_GAMMA(tex_col.rgb);
 
 
    float3 normal = lerp((2.0f * tex2D(NormalTextureSampler, In.Tex0.xy * map_normal_detail_factor).rgb - 1.0f), 1.0f, In.Tex0.z);
 
    /*if (In.Tex0.z == 1)
    {
        normal = 1.0;
    }*/
 
    //float wNdotSun = saturate(dot(vWorldN, -vSunDir));
    //Out.SunLight = (wNdotSun) * vSunColor * vMaterialColor * vColor;
    float4 In_SunLight = saturate(dot(normal, In.SunLightDir)) * vSunColor * vMaterialColor;// * vColor;  vertex color needed??
 
    float sun_amount = 1;
    if ((PcfMode != PCF_NONE))
    {
        sun_amount = GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
    }
    Output.RGBColor =  lerp(tex_col * ((In.Color + In_SunLight * sun_amount)), tex_col, In.Tex0.z);
 
 
    //add fresnel term
    {
        float fresnel = 1-(saturate(dot( normalize(In.ViewDir), normalize(In.WorldNormal))));
        fresnel *= fresnel;
        Output.RGBColor.rgb *= max(0.6,fresnel+0.1);
    }
    // gamma correct
    OUTPUT_GAMMA(Output.RGBColor.rgb);
 
 
    return Output;
}

DEFINE_TECHNIQUES(diffuse_map_bump, vs_main_map_bump, ps_main_map_bump)    //diffuse shader with fresnel effect + bumpmapping(if shader_quality medium)..

//---
struct VS_OUTPUT_MAP_MOUNTAIN
{
    float4 Pos                    : POSITION;
    float  Fog                    : FOG;
 
    float4 Color                : COLOR0;
    float4 Tex0                    : TEXCOORD0;
    float4 SunLight                : TEXCOORD1;
    float4 ShadowTexCoord        : TEXCOORD2;
    float2 ShadowTexelPos        : TEXCOORD3;
 
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;
};

VS_OUTPUT_MAP_MOUNTAIN vs_map_mountain(uniform const int PcfMode, float4 vPosition : POSITION, float3 vNormal : NORMAL,
                                        float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{
    INITIALIZE_OUTPUT(VS_OUTPUT_MAP_MOUNTAIN, Out);

    //dstn Flat Map Experiment
    float4 vPositionOrig = vPosition;
    vPosition.z = map_flatten(vCameraPos.z, vPosition.z);
    Out.Tex0.w = map_smoothstep(vCameraPos.z);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPositionOrig);
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space

    float3 P = mul(matWorldView, vPosition); //position in view space

    Out.Tex0.xy = tc;
    Out.Tex0.z = /*saturate*/(0.7f * (vWorldPos.z - 1.5f));

    float4 diffuse_light = vAmbientColor;
    if (true /*_UseSecondLight*/)
    {
        diffuse_light += vLightColor;
    }

    //directional lights, compute diffuse color
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;

    //apply material color
    //    Out.Color = min(1, vMaterialColor * vColor * diffuse_light);
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //shadow mapping variables
    float wNdotSun = saturate(dot(vWorldN, -vSunDir));
    Out.SunLight = (wNdotSun) * vSunColor;
    if (PcfMode != PCF_NONE)
    {
        float4 ShadowPos = mul(matSunViewProj, vWorldPos);
        Out.ShadowTexCoord = ShadowPos;
        Out.ShadowTexCoord.z /= ShadowPos.w;
        Out.ShadowTexCoord.w = 1.0f;
        Out.ShadowTexelPos = Out.ShadowTexCoord * fShadowMapSize;
        //shadow mapping variables end
    }
 
 
    Out.ViewDir = normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;
 
 
    //apply fog
    float d = length(P);

    Out.Fog = get_fog_amount_map(d, vWorldPos.z);
    return Out;
}

PS_OUTPUT ps_map_mountain(VS_OUTPUT_MAP_MOUNTAIN In, uniform const int PcfMode)
{
    PS_OUTPUT Output;
 
    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0.xy);
    float4 blend_col = tex2D(Diffuse2Sampler, In.Tex0.xy);
    INPUT_TEX_GAMMA(tex_col.rgb);
 
    tex_col.rgb += saturate(In.Tex0.z * (tex_col.a) - 1.5f);
    tex_col.a = 1.0f;
 
    tex_col = lerp(tex_col, blend_col, In.Tex0.w);
 
    if ((PcfMode != PCF_NONE))
    {
        float sun_amount = GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
        //        sun_amount *= sun_amount;
        Output.RGBColor =  saturate(tex_col) * ((In.Color + In.SunLight * sun_amount));
    }
    else
    {
        Output.RGBColor = saturate(tex_col) * (In.Color + In.SunLight);
    }
 
    {
        float fresnel = 1-(saturate(dot( In.ViewDir, In.WorldNormal)));
    //    fresnel *= fresnel;
        Output.RGBColor.rgb *= max(0.6,fresnel+0.1);
    }
 
 
    // gamma correct
    OUTPUT_GAMMA(Output.RGBColor.rgb);
 
 
    return Output;
}

DEFINE_TECHNIQUES(map_mountain, vs_map_mountain, ps_map_mountain)


//---
struct VS_OUTPUT_MAP_MOUNTAIN_BUMP
{
    float4 Pos                    : POSITION;
    float4 Color                    : COLOR0;
    float4 Tex0                    : TEXCOORD0;
    //float4 SunLight                : TEXCOORD1;
    float4 ShadowTexCoord        : TEXCOORD2;
    float2 ShadowTexelPos        : TEXCOORD3;
    float  Fog                    : FOG;
 
    float3 SunLightDir            : TEXCOORD4;
    float3 SkyLightDir            : TEXCOORD5;
 
    float3 ViewDir                : TEXCOORD6;
    float3 WorldNormal            : TEXCOORD7;
};
VS_OUTPUT_MAP_MOUNTAIN_BUMP vs_map_mountain_bump(uniform const int PcfMode, float4 vPosition : POSITION,
                                                float3 vNormal : NORMAL,  float3 vTangent : TANGENT, float3 vBinormal : BINORMAL,
                                                float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{
    INITIALIZE_OUTPUT(VS_OUTPUT_MAP_MOUNTAIN_BUMP, Out);
 
    //dstn Flat Map Experiment
    float4 vPositionOrig = vPosition;
    vPosition.z = map_flatten(vCameraPos.z, vPosition.z);

    Out.Pos = mul(matWorldViewProj, vPosition);

    float4 vWorldPos = (float4)mul(matWorld,vPositionOrig);
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 vWorld_binormal = normalize(mul((float3x3)matWorld, vBinormal)); //normal in world space
    float3 vWorld_tangent  = normalize(mul((float3x3)matWorld, vTangent)); //normal in world space
    float3x3 TBNMatrix = float3x3(vWorld_tangent, vWorld_binormal, vWorldN);

    float3 P = mul(matWorldView, vPosition); //position in view space

    Out.Tex0.xy = tc;
    Out.Tex0.z = /*saturate*/(0.7f * (vWorldPos.z - 1.5f));
    Out.Tex0.w = map_smoothstep(vCameraPos.z);

    float4 diffuse_light = vAmbientColor;
    if (true /*_UseSecondLight*/)
    {
        diffuse_light += vLightColor;
    }

    //directional lights, compute diffuse color
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;

    //apply material color
    //    Out.Color = min(1, vMaterialColor * vColor * diffuse_light);
    Out.Color = (vMaterialColor * vColor * diffuse_light);

    //shadow mapping variables
    //float wNdotSun = saturate(dot(vWorldN, -vSunDir));
    //Out.SunLight = (wNdotSun) * vSunColor;
    Out.SunLightDir = normalize(mul(TBNMatrix, -vSunDir));
       
    if (PcfMode != PCF_NONE)
    {
   
        float4 ShadowPos = mul(matSunViewProj, vWorldPos);
        Out.ShadowTexCoord = ShadowPos;
        Out.ShadowTexCoord.z /= ShadowPos.w;
        Out.ShadowTexCoord.w = 1.0f;
        Out.ShadowTexelPos = Out.ShadowTexCoord * fShadowMapSize;
        //shadow mapping variables end
    }
 
 
    Out.ViewDir = normalize(vCameraPos-vWorldPos);
    Out.WorldNormal = vWorldN;
 
 
    //apply fog
    float d = length(P);

    Out.Fog = get_fog_amount_map(d, vWorldPos.z);
    return Out;
}
PS_OUTPUT ps_map_mountain_bump(VS_OUTPUT_MAP_MOUNTAIN_BUMP In, uniform const int PcfMode)
{
    PS_OUTPUT Output;
 
    float4 sample_col = tex2D(MeshTextureSampler, In.Tex0.xy);
 
 
    float4 tex_col = sample_col;
 
    tex_col.rgb += saturate(In.Tex0.z * (sample_col.a) - 1.5f);
    tex_col.a = 1.0f;
    /*
    float snow = In.Tex0.z * (0.1f + sample_col.a) - 1.5f;
    if (snow > 0.5f)
    {
        tex_col = float4(1.0f,1.0f,1.0f,1.0f);
    }
*/
    float4 blend_col = tex2D(Diffuse2Sampler, In.Tex0.xy);
    tex_col = lerp(tex_col, blend_col, In.Tex0.w);
    INPUT_TEX_GAMMA(tex_col.rgb);

    float3 normal = lerp((2.0f * tex2D(NormalTextureSampler, In.Tex0 * map_normal_detail_factor).rgb - 1.0f), 1.0f, In.Tex0.w);
 
    //float wNdotSun = saturate(dot(vWorldN, -vSunDir));
    //Out.SunLight = (wNdotSun) * vSunColor;
    float4 In_SunLight = saturate(dot(normal, In.SunLightDir)) * vSunColor;
 

    if ((PcfMode != PCF_NONE))
    {
        float sun_amount = GetSunAmount(PcfMode, In.ShadowTexCoord, In.ShadowTexelPos);
        //        sun_amount *= sun_amount;
        Output.RGBColor =  lerp(saturate(tex_col) * ((In.Color + In_SunLight * sun_amount)), tex_col, In.Tex0.w);
    }
    else
    {
        Output.RGBColor = lerp(saturate(tex_col) * (In.Color + In_SunLight), tex_col, In.Tex0.w);;
    }
 
    {
        float fresnel = 1-(saturate(dot( In.ViewDir, In.WorldNormal)));
    //    fresnel *= fresnel;
        Output.RGBColor.rgb *= max(0.6,fresnel+0.1);
    }
 
 
    // gamma correct
    OUTPUT_GAMMA(Output.RGBColor.rgb);
 
 
    return Output;
}

DEFINE_TECHNIQUES(map_mountain_bump, vs_map_mountain_bump, ps_map_mountain_bump)

//---
struct VS_OUTPUT_MAP_WATER
{
    float4 Pos           : POSITION;
    float4 Color         : COLOR0;
    float3 Tex0          : TEXCOORD0;
    float3 LightDir         : TEXCOORD1;//light direction for bump
    float3 CameraDir     : TEXCOORD3;//camera direction for bump
    float4 PosWater         : TEXCOORD4;//position according to the water camera
    float  Fog           : FOG;
};
VS_OUTPUT_MAP_WATER vs_map_water (uniform const bool reflections, float4 vPosition : POSITION, float3 vNormal : NORMAL, float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1, float3 vTangent : TANGENT, float3 vBinormal : BINORMAL)
{
    INITIALIZE_OUTPUT(VS_OUTPUT_MAP_WATER, Out);
 
    // dstn Flat Map Experiment
    /*float4 vPositionOrig = vPosition;
    float distance = smoothstep(30, 50, length(mul(matWorldView, vPosition)));
    vPosition.z *= distance * min(vPosition.z, 250) * 0.01f * ((1 - sin(75 * tc.x + time_var)) - (sin(35 * tc.y + 0.5f * time_var)));*/

    float4 vPositionOrig = vPosition;
    vPosition.z = map_flatten(vCameraPos.z, vPosition.z);
 
    Out.Pos = mul(matWorldViewProj, vPosition);
 

    float4 vWorldPos = (float4)mul(matWorld,vPositionOrig);
    float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
    float3 vWorld_binormal = normalize(mul((float3x3)matWorld, vBinormal)); //normal in world space
    float3 vWorld_tangent  = normalize(mul((float3x3)matWorld, vTangent)); //normal in world space
    float3x3 TBNMatrix = float3x3(vWorld_tangent, vWorld_binormal, vWorldN);

    float3 P = mul(matWorldView, vPosition); //position in view space

    Out.Tex0.z =  map_smoothstep(vCameraPos.z);
    Out.Tex0.xy = tc + texture_offset.xy;
 
 

    float4 diffuse_light = vAmbientColor + vLightColor;

    //directional lights, compute diffuse color
    diffuse_light += saturate(dot(vWorldN, -vSkyLightDir)) * vSkyLightColor;

    float wNdotSun = max(-0.0001f, dot(vWorldN, -vSunDir));
    diffuse_light += (wNdotSun) * vSunColor;

    //apply material color
    //    Out.Color = min(1, vMaterialColor * vColor * diffuse_light);
    Out.Color = (vMaterialColor * vColor) * diffuse_light;
 
    //Out.Color.rgb += Out.Pos.y;
 
 
    if(reflections)
    {
        float4 water_pos = mul(matWaterViewProj, vWorldPos);
        Out.PosWater.xy = (float2(water_pos.x, -water_pos.y)+water_pos.w)/2;
        Out.PosWater.xy += (vDepthRT_HalfPixel_ViewportSizeInv.xy * water_pos.w);
        Out.PosWater.zw = water_pos.zw;
    }
 
    {
        //float3 vWorldN = float3(0,0,1); //vNormal;
        //normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
        //float3 vWorld_tangent  = float3(1,0,0);
        //float3 vWorld_tangent  = normalize(mul((float3x3)matWorld, vTangent)); //normal in world space
        //float3 vWorld_binormal = float3(0,1,0);
        // float3 vWorld_binormal = normalize(cross(vWorld_tangent, vNormal)); //normalize(mul((float3x3)matWorld, vBinormal)); //normal in world space

        float3x3 TBNMatrix = float3x3(vWorld_tangent, vWorld_binormal, vWorldN);

        float3 point_to_camera_normal = normalize(vCameraPos.xyz - vWorldPos.xyz);
        Out.CameraDir = mul(TBNMatrix, -point_to_camera_normal);
        Out.LightDir = mul(TBNMatrix, -vSunDir);
    }
 
    /*
    float terminator = smoothstep(30, 50, Out.Pos.z);
 
 
    //Vertical Wave Displacements: If more than 25 units away from camera and camera isn't aiming down
    if(Out.Pos.z > 25 && vCameraPos.z < 30)
    Out.Pos.y += terminator * min(Out.Pos.z, 250) * 0.01f * ((1 - sin(75 * vWorldPos.x + time_var)) - (sin(35 * vWorldPos.y + 0.5f * time_var)));
    else
    Out.Pos.y += 0.01f * ((1 - sin(75 * vWorldPos.x + time_var)) - (sin(35 * vWorldPos.y + 0.5f * time_var)));
    */
 
    float height = 1 / (Out.Pos.z / 10);

    //apply fog
    float d = length(P);
    Out.Fog = get_fog_amount_map(d, vWorldPos.z);
 
    return Out;
}
PS_OUTPUT ps_map_water(uniform const bool reflections, VS_OUTPUT_MAP_WATER In)
{
    PS_OUTPUT Output;
    Output.RGBColor =  In.Color;

    float4 tex_col = tex2D(MeshTextureSampler, In.Tex0.xy);
    float4 blend_col = tex2D(Diffuse2Sampler, In.Tex0.xy);
    tex_col.rgb = lerp(tex_col, blend_col * /*float3(0.25,0.35,0.5)*/ 0.85, In.Tex0.z);
    INPUT_TEX_GAMMA(tex_col.rgb);
 
    /////////////////////
    float3 normal;
    normal.xy = (2.0f * tex2D(NormalTextureSampler, In.Tex0 * 8).ag - 1.0f);
    normal.z = sqrt(1.0f - dot(normal.xy, normal.xy));
 
    float NdotL = saturate( dot(normal, In.LightDir) );
    float3 vView = normalize(In.CameraDir);

    // Fresnel term
    float fresnel = 1-(saturate(dot(vView, normal)));
    fresnel = 0.0204f + 0.9796 * (fresnel * fresnel * fresnel * fresnel * fresnel);
    Output.RGBColor.rgb += fresnel * In.Color.rgb;
    /////////////////////
   
    if(reflections)
    {
        //float4 tex = tex2D(ReflectionTextureSampler, g_HalfPixel_ViewportSizeInv.xy + 0.25f * normal.xy + float2(0.5f + 0.5f * (In.PosWater.x / In.PosWater.w), 0.5f - 0.5f * (In.PosWater.y / In.PosWater.w)));
        In.PosWater.xy += 0.35f * normal.xy;
        float4 tex = tex2Dproj(ReflectionTextureSampler, In.PosWater);
        INPUT_OUTPUT_GAMMA(tex.rgb);
        tex.rgb = min(tex.rgb, 4.0f);
   
        Output.RGBColor.rgb *= NdotL * lerp(tex_col.rgb, tex.rgb, reflection_factor);
    }
    else
    {
        Output.RGBColor.rgb *= tex_col.rgb;
    }
 
    if (In.CameraDir.z > 0.5)
    {
    Output.RGBColor.rgb = tex2Dproj(ReflectionTextureSampler, In.PosWater);
    }

    //Output.RGBColor.rgb = lerp(Output.RGBColor.rgb, blend_col.rgb * float3(0.5,0.5,1), In.Tex0.z);
    OUTPUT_GAMMA(Output.RGBColor.rgb);    //0.5 * normal + 0.5; //
    //Output.RGBColor.rgb = In.Color.rgb;
 
    Output.RGBColor.a = In.Color.a * tex_col.a;
 
    return Output;
}

technique map_water
{
    pass P0
    {
        VertexShader = compile vs_2_0 vs_map_water(false);
        PixelShader = compile ps_2_a ps_map_water(false);
    }
}
technique map_water_high
{
    pass P0
    {
        VertexShader = compile vs_2_0 vs_map_water(true);
        PixelShader = compile ps_2_a ps_map_water(true);
    }
}
#endif
 
Last edited:
Back
Top Bottom