Improved terrain splatting and parallax mapping

I’ve modified the terrain texture blending to allow nicer looking blending of the different texture layers.
The idea behind this is to treat the different layers you paint in the terrain editor as height not as alpha, this height value gets modified by the height map of the terrain textures(I’m currently using the diffuse textures alpha channel for this). The heighest value will then be drawn, with blending if textures are at similar heights.

Normal terrain splatting:

Height based terrain splatting:

The algorithm to determine the visibility of a texture looks like this:
[java]
// global variable to compare heights of different alpha maps and textures.
float height = 0.0;
// defines strength of the alpha in comparison to the blending textures.
// calculate visibility of texture.
float calculateAlphaBlend(in float alphaMap, in float textureBlend){
// combined height of alphaMap and texture.
textureBlend += alphaMap * 2.0;

// a blending value to soften transition between textures
alphaMap = (clamp(textureBlend - height, -0.125,0.125)+0.125)*4.0;

if (textureBlend > height)
{
    height = textureBlend;
}
return alphaMap;

}
[/java]

The parallax mapping part is just the already existing parallax mapping with a modified texture look up because it has to sample all texture layers.

I’ve added a few new parameters to the terrain material:
[java]
Boolean blendWithAlpha - this acitvates the new height based terrain splatting.

Boolean parallaxMapping - this activates parallax mapping.

Boolean steepParallax - switches between parallax mapping and steep parallax mapping

Float parallaxHeight - sets the strength of the parallax effect

Float parallaxSamples - is used to set the number of samples for steep parallax mapping
[/java]

Now the actual Code for the modified TerrainLighting.j3md and TerrainLighting.frag
TerrainLighting.j3md:
[java]
// NOTE: Doesn’t support OpenGL1
MaterialDef Terrain Lighting {

MaterialParameters {

    // use tri-planar mapping
    Boolean useTriPlanarMapping

    // Use ward specular instead of phong
    Boolean WardIso

    // Are we rendering TerrainGrid
    Boolean isTerrainGrid

    // Use alpha channel of diffuse texture to blend with structure
    Boolean blendWithAlpha

    // Use alpha channel of diffuse texture for parallax mapping;
    Boolean parallaxMapping

    Boolean steepParallax

    Float ParallaxHeight : 0.005

    Float ParallaxSamples : 16

    // Ambient color
    Color Ambient

    // Diffuse color
    Color Diffuse

    // Specular color
    Color Specular

    // Specular power/shininess
    Float Shininess : 0

    // Texture map #0
    Texture2D DiffuseMap
    Float DiffuseMap_0_scale
    Texture2D NormalMap

    // Texture map #1
    Texture2D DiffuseMap_1
    Float DiffuseMap_1_scale
    Texture2D NormalMap_1

    // Texture map #2
    Texture2D DiffuseMap_2
    Float DiffuseMap_2_scale
    Texture2D NormalMap_2

    // Texture map #3
    Texture2D DiffuseMap_3
    Float DiffuseMap_3_scale
    Texture2D NormalMap_3

    // Texture map #4
    Texture2D DiffuseMap_4
    Float DiffuseMap_4_scale
    Texture2D NormalMap_4

    // Texture map #5
    Texture2D DiffuseMap_5
    Float DiffuseMap_5_scale
    Texture2D NormalMap_5

    // Texture map #6
    Texture2D DiffuseMap_6
    Float DiffuseMap_6_scale
    Texture2D NormalMap_6

    // Texture map #7
    Texture2D DiffuseMap_7
    Float DiffuseMap_7_scale
    Texture2D NormalMap_7

    // Texture map #8
    Texture2D DiffuseMap_8
    Float DiffuseMap_8_scale
    Texture2D NormalMap_8

    // Texture map #9
    Texture2D DiffuseMap_9
    Float DiffuseMap_9_scale
    Texture2D NormalMap_9

    // Texture map #10
    Texture2D DiffuseMap_10
    Float DiffuseMap_10_scale
    Texture2D NormalMap_10

    // Texture map #11
    Texture2D DiffuseMap_11
    Float DiffuseMap_11_scale
    Texture2D NormalMap_11


    // Specular/gloss map
    Texture2D SpecularMap


    // Texture that specifies alpha values
    Texture2D AlphaMap
    Texture2D AlphaMap_1
    Texture2D AlphaMap_2

    // Texture of the glowing parts of the material
    Texture2D GlowMap

    // The glow color of the object
    Color GlowColor
}

Technique {

    LightMode MultiPass

    VertexShader GLSL100:   MatDefs/Terrain/TerrainLighting.vert
    FragmentShader GLSL100: MatDefs/Terrain/TerrainLighting.frag

    WorldParameters {
        WorldViewProjectionMatrix
        NormalMatrix
        WorldViewMatrix
        ViewMatrix
    }

    Defines {
        TRI_PLANAR_MAPPING : useTriPlanarMapping
        TERRAIN_GRID : isTerrainGrid
        WARDISO   : WardIso
        BLENDWITHALPHA : blendWithAlpha
        PARALLAXMAPPING : parallaxMapping
        STEEPPARALLAX : steepParallax 

        DIFFUSEMAP : DiffuseMap
        DIFFUSEMAP_1 : DiffuseMap_1
        DIFFUSEMAP_2 : DiffuseMap_2
        DIFFUSEMAP_3 : DiffuseMap_3
        DIFFUSEMAP_4 : DiffuseMap_4
        DIFFUSEMAP_5 : DiffuseMap_5
        DIFFUSEMAP_6 : DiffuseMap_6
        DIFFUSEMAP_7 : DiffuseMap_7
        DIFFUSEMAP_8 : DiffuseMap_8
        DIFFUSEMAP_9 : DiffuseMap_9
        DIFFUSEMAP_10 : DiffuseMap_10
        DIFFUSEMAP_11 : DiffuseMap_11
        NORMALMAP : NormalMap
        NORMALMAP_1 : NormalMap_1
        NORMALMAP_2 : NormalMap_2
        NORMALMAP_3 : NormalMap_3
        NORMALMAP_4 : NormalMap_4
        NORMALMAP_5 : NormalMap_5
        NORMALMAP_6 : NormalMap_6
        NORMALMAP_7 : NormalMap_7
        NORMALMAP_8 : NormalMap_8
        NORMALMAP_9 : NormalMap_9
        NORMALMAP_10 : NormalMap_10
        NORMALMAP_11 : NormalMap_11
        SPECULARMAP : SpecularMap
        ALPHAMAP : AlphaMap
        ALPHAMAP_1 : AlphaMap_1
        ALPHAMAP_2 : AlphaMap_2
        DIFFUSEMAP_0_SCALE : DiffuseMap_0_scale
        DIFFUSEMAP_1_SCALE : DiffuseMap_1_scale
        DIFFUSEMAP_2_SCALE : DiffuseMap_2_scale
        DIFFUSEMAP_3_SCALE : DiffuseMap_3_scale
        DIFFUSEMAP_4_SCALE : DiffuseMap_4_scale
        DIFFUSEMAP_5_SCALE : DiffuseMap_5_scale
        DIFFUSEMAP_6_SCALE : DiffuseMap_6_scale
        DIFFUSEMAP_7_SCALE : DiffuseMap_7_scale
        DIFFUSEMAP_8_SCALE : DiffuseMap_8_scale
        DIFFUSEMAP_9_SCALE : DiffuseMap_9_scale
        DIFFUSEMAP_10_SCALE : DiffuseMap_10_scale
        DIFFUSEMAP_11_SCALE : DiffuseMap_11_scale
    }
}

Technique PreShadow {

    VertexShader GLSL100 :   Common/MatDefs/Shadow/PreShadow.vert
    FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag

    WorldParameters {
        WorldViewProjectionMatrix
        WorldViewMatrix
    }

    Defines {
        DIFFUSEMAP_ALPHA : DiffuseMap
    }

    RenderState {
        FaceCull Off
        DepthTest On
        DepthWrite On
        PolyOffset 5 0
        ColorWrite Off
    }

}

Technique PreNormalPass {

    VertexShader GLSL100 :   Common/MatDefs/SSAO/normal.vert
    FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag

    WorldParameters {
        WorldViewProjectionMatrix
        WorldViewMatrix
        NormalMatrix
    }

    Defines {
        DIFFUSEMAP_ALPHA : DiffuseMap
    }

    RenderState {

    }

}

Technique GBuf {

    VertexShader GLSL100:   Common/MatDefs/Light/GBuf.vert
    FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag

    WorldParameters {
        WorldViewProjectionMatrix
        WorldMatrix
    }

    Defines {
        VERTEX_COLOR : UseVertexColor
        MATERIAL_COLORS : UseMaterialColors
        V_TANGENT : VTangent
        MINNAERT  : Minnaert
        WARDISO   : WardIso

        DIFFUSEMAP : DiffuseMap
        NORMALMAP : NormalMap
        SPECULARMAP : SpecularMap
        PARALLAXMAP : ParallaxMap
    }
}

Technique {
    LightMode FixedPipeline
}

Technique Glow {

    VertexShader GLSL100:   Common/MatDefs/Misc/Unshaded.vert
    FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag

    WorldParameters {
        WorldViewProjectionMatrix
    }

    Defines {
        HAS_GLOWMAP : GlowMap
        HAS_GLOWCOLOR : GlowColor
    }
}

}
[/java]

TerrainLighting.frag:
[java]

uniform float m_Shininess;
uniform vec4 g_LightDirection;

varying vec4 AmbientSum;
varying vec4 DiffuseSum;
varying vec4 SpecularSum;

varying vec3 vNormal;
varying vec2 texCoord;
varying vec3 vPosition;
varying vec3 vnPosition;
varying vec3 vViewDir;
varying vec4 vLightDir;
varying vec4 vnLightDir;
varying vec3 lightVec;

#ifdef PARALLAXMAPPING
uniform float m_ParallaxHeight;
#ifdef STEEPPARALLAX
uniform float m_ParallaxSamples;
#endif
#endif
#ifdef DIFFUSEMAP
uniform sampler2D m_DiffuseMap;
#endif
#ifdef DIFFUSEMAP_1
uniform sampler2D m_DiffuseMap_1;
#endif
#ifdef DIFFUSEMAP_2
uniform sampler2D m_DiffuseMap_2;
#endif
#ifdef DIFFUSEMAP_3
uniform sampler2D m_DiffuseMap_3;
#endif
#ifdef DIFFUSEMAP_4
uniform sampler2D m_DiffuseMap_4;
#endif
#ifdef DIFFUSEMAP_5
uniform sampler2D m_DiffuseMap_5;
#endif
#ifdef DIFFUSEMAP_6
uniform sampler2D m_DiffuseMap_6;
#endif
#ifdef DIFFUSEMAP_7
uniform sampler2D m_DiffuseMap_7;
#endif
#ifdef DIFFUSEMAP_8
uniform sampler2D m_DiffuseMap_8;
#endif
#ifdef DIFFUSEMAP_9
uniform sampler2D m_DiffuseMap_9;
#endif
#ifdef DIFFUSEMAP_10
uniform sampler2D m_DiffuseMap_10;
#endif
#ifdef DIFFUSEMAP_11
uniform sampler2D m_DiffuseMap_11;
#endif

#ifdef DIFFUSEMAP_0_SCALE
uniform float m_DiffuseMap_0_scale;
#endif
#ifdef DIFFUSEMAP_1_SCALE
uniform float m_DiffuseMap_1_scale;
#endif
#ifdef DIFFUSEMAP_2_SCALE
uniform float m_DiffuseMap_2_scale;
#endif
#ifdef DIFFUSEMAP_3_SCALE
uniform float m_DiffuseMap_3_scale;
#endif
#ifdef DIFFUSEMAP_4_SCALE
uniform float m_DiffuseMap_4_scale;
#endif
#ifdef DIFFUSEMAP_5_SCALE
uniform float m_DiffuseMap_5_scale;
#endif
#ifdef DIFFUSEMAP_6_SCALE
uniform float m_DiffuseMap_6_scale;
#endif
#ifdef DIFFUSEMAP_7_SCALE
uniform float m_DiffuseMap_7_scale;
#endif
#ifdef DIFFUSEMAP_8_SCALE
uniform float m_DiffuseMap_8_scale;
#endif
#ifdef DIFFUSEMAP_9_SCALE
uniform float m_DiffuseMap_9_scale;
#endif
#ifdef DIFFUSEMAP_10_SCALE
uniform float m_DiffuseMap_10_scale;
#endif
#ifdef DIFFUSEMAP_11_SCALE
uniform float m_DiffuseMap_11_scale;
#endif

#ifdef ALPHAMAP
uniform sampler2D m_AlphaMap;
#endif
#ifdef ALPHAMAP_1
uniform sampler2D m_AlphaMap_1;
#endif
#ifdef ALPHAMAP_2
uniform sampler2D m_AlphaMap_2;
#endif

#ifdef NORMALMAP
uniform sampler2D m_NormalMap;
#endif
#ifdef NORMALMAP_1
uniform sampler2D m_NormalMap_1;
#endif
#ifdef NORMALMAP_2
uniform sampler2D m_NormalMap_2;
#endif
#ifdef NORMALMAP_3
uniform sampler2D m_NormalMap_3;
#endif
#ifdef NORMALMAP_4
uniform sampler2D m_NormalMap_4;
#endif
#ifdef NORMALMAP_5
uniform sampler2D m_NormalMap_5;
#endif
#ifdef NORMALMAP_6
uniform sampler2D m_NormalMap_6;
#endif
#ifdef NORMALMAP_7
uniform sampler2D m_NormalMap_7;
#endif
#ifdef NORMALMAP_8
uniform sampler2D m_NormalMap_8;
#endif
#ifdef NORMALMAP_9
uniform sampler2D m_NormalMap_9;
#endif
#ifdef NORMALMAP_10
uniform sampler2D m_NormalMap_10;
#endif
#ifdef NORMALMAP_11
uniform sampler2D m_NormalMap_11;
#endif

#ifdef TRI_PLANAR_MAPPING
varying vec4 wVertex;
varying vec3 wNormal;
#endif

float tangDot(in vec3 v1, in vec3 v2){
float d = dot(v1,v2);
#ifdef V_TANGENT
d = 1.0 - d*d;
return step(0.0, d) * sqrt(d);
#else
return d;
#endif
}

float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
return max(0.0, dot(norm, lightdir));
}

float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
#ifdef WARDISO
// Isotropic Ward
vec3 halfVec = normalize(viewdir + lightdir);
float NdotH = max(0.001, tangDot(norm, halfVec));
float NdotV = max(0.001, tangDot(norm, viewdir));
float NdotL = max(0.001, tangDot(norm, lightdir));
float a = tan(acos(NdotH));
float p = max(shiny/128.0, 0.001);
return NdotL * (1.0 / (4.03.14159265pp)) * (exp(-(aa)/(p*p)) / (sqrt(NdotV * NdotL)));
#else
// Standard Phong
vec3 R = reflect(-lightdir, norm);
return pow(max(tangDot(R, viewdir), 0.0), shiny);
#endif
}

vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);

if (m_Shininess <= 1.0) {
specularFactor = 0.0; // should be one instruction on most cards …
}

float att = vLightDir.w;

return vec2(diffuseFactor, specularFactor) * vec2(att);
}

#ifdef BLENDWITHALPHA
// global variable to compare heights of different alpha maps and textures.
float height = 0.0;
// defines strength of the alpha in comparison to the blending textures.
// calculate visibility of texture.
float calculateAlphaBlend(in float alphaMap, in float textureBlend){
// combined height of alphaMap and texture.
textureBlend += alphaMap * 2.0;

// a blending value to soften transition between textures
alphaMap = (clamp(textureBlend - height, -0.125,0.125)+0.125)*4.0;

if (textureBlend &gt; height)
{
    height = textureBlend;
}
return alphaMap;

}
#endif

#ifdef ALPHAMAP

vec4 calculateDiffuseBlend(in vec2 texCoord) {
vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );

#ifdef ALPHAMAP_1
  vec4 alphaBlend1   = texture2D( m_AlphaMap_1, texCoord.xy );
#endif
#ifdef ALPHAMAP_2
  vec4 alphaBlend2   = texture2D( m_AlphaMap_2, texCoord.xy );
#endif

vec4 diffuseColor = texture2D(m_DiffuseMap, texCoord * m_DiffuseMap_0_scale);
#ifdef BLENDWITHALPHA
  height = 0.0;
  alphaBlend.r = calculateAlphaBlend(alphaBlend.r, diffuseColor.a);
#endif
diffuseColor *= alphaBlend.r;
#ifdef DIFFUSEMAP_1
  vec4 diffuseColor1 = texture2D(m_DiffuseMap_1, texCoord * m_DiffuseMap_1_scale);
  #ifdef BLENDWITHALPHA
    alphaBlend.g = calculateAlphaBlend(alphaBlend.g, diffuseColor1.a);
  #endif
  diffuseColor = mix( diffuseColor, diffuseColor1, alphaBlend.g);
  #ifdef DIFFUSEMAP_2
    vec4 diffuseColor2 = texture2D(m_DiffuseMap_2, texCoord * m_DiffuseMap_2_scale);
    #ifdef BLENDWITHALPHA
      alphaBlend.b = calculateAlphaBlend(alphaBlend.b, diffuseColor2.a);
    #endif
    diffuseColor = mix( diffuseColor, diffuseColor2, alphaBlend.b );
    #ifdef DIFFUSEMAP_3
      vec4 diffuseColor3 = texture2D(m_DiffuseMap_3, texCoord * m_DiffuseMap_3_scale);
      #ifdef BLENDWITHALPHA
        alphaBlend.a = calculateAlphaBlend(alphaBlend.a, diffuseColor3.a);
      #endif
      diffuseColor = mix( diffuseColor, diffuseColor3, alphaBlend.a );
      #ifdef ALPHAMAP_1
        #ifdef DIFFUSEMAP_4
          vec4 diffuseColor4 = texture2D(m_DiffuseMap_4, texCoord * m_DiffuseMap_4_scale);
          #ifdef BLENDWITHALPHA
            alphaBlend1.r = calculateAlphaBlend(alphaBlend1.r, diffuseColor4.a);
          #endif
          diffuseColor = mix( diffuseColor, diffuseColor4, alphaBlend1.r );
          #ifdef DIFFUSEMAP_5
            vec4 diffuseColor5 = texture2D(m_DiffuseMap_5, texCoord * m_DiffuseMap_5_scale);
            #ifdef BLENDWITHALPHA
              alphaBlend1.g = calculateAlphaBlend(alphaBlend1.g, diffuseColor5.a);
            #endif
            diffuseColor = mix( diffuseColor, diffuseColor5, alphaBlend1.g );
            #ifdef DIFFUSEMAP_6
              vec4 diffuseColor6 = texture2D(m_DiffuseMap_6, texCoord * m_DiffuseMap_6_scale);
              #ifdef BLENDWITHALPHA
                alphaBlend1.b = calculateAlphaBlend(alphaBlend1.b, diffuseColor6.a);
              #endif
              diffuseColor = mix( diffuseColor, diffuseColor6, alphaBlend1.b );
              #ifdef DIFFUSEMAP_7
                vec4 diffuseColor7 = texture2D(m_DiffuseMap_7, texCoord * m_DiffuseMap_7_scale);
                #ifdef BLENDWITHALPHA
                  alphaBlend1.a = calculateAlphaBlend(alphaBlend1.a, diffuseColor7.a);
                #endif
                diffuseColor = mix( diffuseColor, diffuseColor7, alphaBlend1.a );
                #ifdef ALPHAMAP_2
                  #ifdef DIFFUSEMAP_8
                    vec4 diffuseColor8 = texture2D(m_DiffuseMap_8, texCoord * m_DiffuseMap_8_scale);
                    #ifdef BLENDWITHALPHA
                      alphaBlend2.r = calculateAlphaBlend(alphaBlend2.r, diffuseColor8.a);
                    #endif
                    diffuseColor = mix( diffuseColor, diffuseColor8, alphaBlend2.r );
                    #ifdef DIFFUSEMAP_9
                      vec4 diffuseColor9 = texture2D(m_DiffuseMap_9, texCoord * m_DiffuseMap_9_scale);
                      #ifdef BLENDWITHALPHA
                        alphaBlend2.g = calculateAlphaBlend(alphaBlend2.g, diffuseColor9.a);
                      #endif
                      diffuseColor = mix( diffuseColor, diffuseColor9, alphaBlend2.g );
                      #ifdef DIFFUSEMAP_10
                        vec4 diffuseColor10 = texture2D(m_DiffuseMap_10, texCoord * m_DiffuseMap_10_scale);
                        #ifdef BLENDWITHALPHA
                          alphaBlend2.b = calculateAlphaBlend(alphaBlend2.b, diffuseColor10.a);
                        #endif
                        diffuseColor = mix( diffuseColor, diffuseColor10, alphaBlend2.b );
                        #ifdef DIFFUSEMAP_11
                          vec4 diffuseColor11 = texture2D(m_DiffuseMap_11, texCoord * m_DiffuseMap_11_scale);
                          #ifdef BLENDWITHALPHA
                            alphaBlend2.a = calculateAlphaBlend(alphaBlend2.a, diffuseColor11.a);
                          #endif
                          diffuseColor = mix( diffuseColor, diffuseColor11, alphaBlend2.a );
                        #endif
                      #endif
                    #endif
                  #endif
                #endif
              #endif
            #endif
          #endif
        #endif
      #endif
    #endif
  #endif
#endif
return diffuseColor;

}

vec3 calculateNormal(in vec2 texCoord) {
vec3 normal = vec3(0.5,0.5,1.0);
vec3 n = vec3(0.0,0.0,0.0);

vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );

#ifdef ALPHAMAP_1
  vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
#endif
#ifdef ALPHAMAP_2
  vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
#endif

#ifdef BLENDWITHALPHA
  height = 0.0;
  alphaBlend.r = calculateAlphaBlend(alphaBlend.r, texture2D(m_DiffuseMap, texCoord * m_DiffuseMap_0_scale).a);
#endif
#ifdef NORMALMAP
  n = texture2D(m_NormalMap, texCoord * m_DiffuseMap_0_scale).xyz;
  normal = mix (normal, n, alphaBlend.r);
#endif

#if defined(DIFFUSEMAP_1)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend.g = calculateAlphaBlend(alphaBlend.g, texture2D(m_DiffuseMap_1, texCoord * m_DiffuseMap_1_scale).a);
#endif
#ifdef NORMALMAP_1
  n = texture2D(m_NormalMap_1, texCoord * m_DiffuseMap_1_scale).xyz;
  normal = mix (normal, n, alphaBlend.g);
#else
#if defined(DIFFUSEMAP_1)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend.g);
#endif
#endif

#if defined(DIFFUSEMAP_2)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend.b = calculateAlphaBlend(alphaBlend.b, texture2D(m_DiffuseMap_2, texCoord * m_DiffuseMap_2_scale).a);
#endif
#ifdef NORMALMAP_2
  n = texture2D(m_NormalMap_2, texCoord * m_DiffuseMap_2_scale).xyz;
  normal = mix (normal, n, alphaBlend.b);
#else
#if defined(DIFFUSEMAP_2)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend.b);
#endif
#endif
#if defined(DIFFUSEMAP_3)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend.a = calculateAlphaBlend(alphaBlend.a, texture2D(m_DiffuseMap_3, texCoord * m_DiffuseMap_3_scale).a);
#endif
#ifdef NORMALMAP_3
  n = texture2D(m_NormalMap_3, texCoord * m_DiffuseMap_3_scale).xyz;
  normal = mix (normal, n, alphaBlend.a);
#else
#if defined(DIFFUSEMAP_3)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend.a);
#endif
#endif
#ifdef ALPHAMAP_1
#if defined(DIFFUSEMAP_4)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend1.r = calculateAlphaBlend(alphaBlend1.r, texture2D(m_DiffuseMap_4, texCoord * m_DiffuseMap_4_scale).a);
#endif
#ifdef NORMALMAP_4
  n = texture2D(m_NormalMap_4, texCoord * m_DiffuseMap_4_scale).xyz;
  normal = mix (normal, n, alphaBlend1.r);
#else
#if defined(DIFFUSEMAP_4)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend1.r);
#endif
#endif
#if defined(DIFFUSEMAP_5)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend1.g = calculateAlphaBlend(alphaBlend1.g, texture2D(m_DiffuseMap_5, texCoord * m_DiffuseMap_5_scale).a);
#endif
#ifdef NORMALMAP_5
  n = texture2D(m_NormalMap_5, texCoord * m_DiffuseMap_5_scale).xyz;
  normal = mix (normal, n, alphaBlend1.g);
#else
#if defined(DIFFUSEMAP_5)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend1.g);
#endif
#endif
#if defined(DIFFUSEMAP_6)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend1.b = calculateAlphaBlend(alphaBlend1.b, texture2D(m_DiffuseMap_6, texCoord * m_DiffuseMap_6_scale).a);
#endif
#ifdef NORMALMAP_6
  n = texture2D(m_NormalMap_6, texCoord * m_DiffuseMap_6_scale).xyz;
  normal = mix (normal, n, alphaBlend1.b);
#else
#if defined(DIFFUSEMAP_6)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend1.b);
#endif
#endif
#if defined(DIFFUSEMAP_7)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend1.a = calculateAlphaBlend(alphaBlend1.a, texture2D(m_DiffuseMap_7, texCoord * m_DiffuseMap_7_scale).a);
#endif
#ifdef NORMALMAP_7
  n = texture2D(m_NormalMap_7, texCoord * m_DiffuseMap_7_scale).xyz;
  normal = mix (normal, n, alphaBlend1.a);
#else
#if defined(DIFFUSEMAP_7)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend1.a);
#endif
#endif
#endif
#ifdef ALPHAMAP_2
#if defined(DIFFUSEMAP_8)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend2.r = calculateAlphaBlend(alphaBlend2.r, texture2D(m_DiffuseMap_8, texCoord * m_DiffuseMap_8_scale).a);
#endif
#ifdef NORMALMAP_8
  n = texture2D(m_NormalMap_8, texCoord * m_DiffuseMap_8_scale).xyz;
  normal = mix (normal, n, alphaBlend2.r);
#else
#if defined(DIFFUSEMAP_8)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend2.r);
#endif
#endif
#if defined(DIFFUSEMAP_9)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend2.g = calculateAlphaBlend(alphaBlend2.g, texture2D(m_DiffuseMap_9, texCoord * m_DiffuseMap_9_scale).a);
#endif
#ifdef NORMALMAP_9
  n = texture2D(m_NormalMap_9, texCoord * m_DiffuseMap_9_scale);
  normal = mix (normal, n, alphaBlend2.g);
#else
#if defined(DIFFUSEMAP_9)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend2.g);
#endif
#endif
#if defined(DIFFUSEMAP_10)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend2.b = calculateAlphaBlend(alphaBlend2.b, texture2D(m_DiffuseMap_10, texCoord * m_DiffuseMap_10_scale).a);
#endif
#ifdef NORMALMAP_10
  n = texture2D(m_NormalMap_10, texCoord * m_DiffuseMap_10_scale);
  normal = mix (normal, n, alphaBlend2.b);
#else
#if defined(DIFFUSEMAP_10)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend2.b);
#endif
#endif
#if defined(DIFFUSEMAP_11)&amp;&amp; defined(BLENDWITHALPHA)
  alphaBlend2.a = calculateAlphaBlend(alphaBlend2.a, texture2D(m_DiffuseMap_11, texCoord * m_DiffuseMap_11_scale).a);
#endif
#ifdef NORMALMAP_11
  n = texture2D(m_NormalMap_11, texCoord * m_DiffuseMap_11_scale);
  normal = mix (normal, n, alphaBlend2.a);
#else
#if defined(DIFFUSEMAP_11)
  normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend2.a);
#endif
#endif
#endif

return normalize(normal.xyz * vec3(2.0) - vec3(1.0));

}

#ifdef TRI_PLANAR_MAPPING

vec4 getTriPlanarBlend(in vec4 coords, in vec3 blending, in sampler2D map, in float scale) {
  vec4 col1 = texture2D( map, coords.yz * scale);
  vec4 col2 = texture2D( map, coords.xz * scale);
  vec4 col3 = texture2D( map, coords.xy * scale);
  // blend the results of the 3 planar projections.
  vec4 tex = col1 * blending.x + col2 * blending.y + col3 * blending.z;
  return tex;
}

vec4 calculateTriPlanarDiffuseBlend(in vec3 wNorm, in vec4 wVert, in vec2 texCoord) {
    // tri-planar texture bending factor for this fragment's normal
    vec3 blending = abs( wNorm );
    blending = (blending -0.2) * 0.7;
    blending = normalize(max(blending, 0.00001));      // Force weights to sum to 1.0 (very important!)
    float b = (blending.x + blending.y + blending.z);
    blending /= vec3(b, b, b);

    // texture coords
    vec4 coords = wVert;

    // blend the results of the 3 planar projections.
    vec4 tex0 = getTriPlanarBlend(coords, blending, m_DiffuseMap, m_DiffuseMap_0_scale);

    #ifdef DIFFUSEMAP_1
      // blend the results of the 3 planar projections.
      vec4 tex1 = getTriPlanarBlend(coords, blending, m_DiffuseMap_1, m_DiffuseMap_1_scale);
    #endif
    #ifdef DIFFUSEMAP_2
      // blend the results of the 3 planar projections.
      vec4 tex2 = getTriPlanarBlend(coords, blending, m_DiffuseMap_2, m_DiffuseMap_2_scale);
    #endif
    #ifdef DIFFUSEMAP_3
      // blend the results of the 3 planar projections.
      vec4 tex3 = getTriPlanarBlend(coords, blending, m_DiffuseMap_3, m_DiffuseMap_3_scale);
    #endif
    #ifdef DIFFUSEMAP_4
      // blend the results of the 3 planar projections.
      vec4 tex4 = getTriPlanarBlend(coords, blending, m_DiffuseMap_4, m_DiffuseMap_4_scale);
    #endif
    #ifdef DIFFUSEMAP_5
      // blend the results of the 3 planar projections.
      vec4 tex5 = getTriPlanarBlend(coords, blending, m_DiffuseMap_5, m_DiffuseMap_5_scale);
    #endif
    #ifdef DIFFUSEMAP_6
      // blend the results of the 3 planar projections.
      vec4 tex6 = getTriPlanarBlend(coords, blending, m_DiffuseMap_6, m_DiffuseMap_6_scale);
    #endif
    #ifdef DIFFUSEMAP_7
      // blend the results of the 3 planar projections.
      vec4 tex7 = getTriPlanarBlend(coords, blending, m_DiffuseMap_7, m_DiffuseMap_7_scale);
    #endif
    #ifdef DIFFUSEMAP_8
      // blend the results of the 3 planar projections.
      vec4 tex8 = getTriPlanarBlend(coords, blending, m_DiffuseMap_8, m_DiffuseMap_8_scale);
    #endif
    #ifdef DIFFUSEMAP_9
      // blend the results of the 3 planar projections.
      vec4 tex9 = getTriPlanarBlend(coords, blending, m_DiffuseMap_9, m_DiffuseMap_9_scale);
    #endif
    #ifdef DIFFUSEMAP_10
      // blend the results of the 3 planar projections.
      vec4 tex10 = getTriPlanarBlend(coords, blending, m_DiffuseMap_10, m_DiffuseMap_10_scale);
    #endif
    #ifdef DIFFUSEMAP_11
      // blend the results of the 3 planar projections.
      vec4 tex11 = getTriPlanarBlend(coords, blending, m_DiffuseMap_11, m_DiffuseMap_11_scale);
    #endif

    vec4 alphaBlend   = texture2D( m_AlphaMap, texCoord.xy );

    #ifdef ALPHAMAP_1
      vec4 alphaBlend1   = texture2D( m_AlphaMap_1, texCoord.xy );
    #endif
    #ifdef ALPHAMAP_2
      vec4 alphaBlend2   = texture2D( m_AlphaMap_2, texCoord.xy );
    #endif

    #ifdef BLENDWITHALPHA
    alphaBlend.r = calculateAlphaBlend(alphaBlend.r, tex0.a);
    #endif
    vec4 diffuseColor = tex0 * alphaBlend.r;
    #ifdef DIFFUSEMAP_1
      #ifdef BLENDWITHALPHA
        alphaBlend.g =  calculateAlphaBlend(alphaBlend.g, tex1.a);
      #endif
      diffuseColor = mix( diffuseColor, tex1, alphaBlend.g );
      #ifdef DIFFUSEMAP_2
        #ifdef BLENDWITHALPHA
          alphaBlend.b = calculateAlphaBlend(alphaBlend.b, tex2.a);
        #endif
        diffuseColor = mix( diffuseColor, tex2, alphaBlend.b );
        #ifdef DIFFUSEMAP_3
          #ifdef BLENDWITHALPHA
            alphaBlend.a = calculateAlphaBlend(alphaBlend.a, tex3.a);
          #endif
          diffuseColor = mix( diffuseColor, tex3, alphaBlend.a );
          #ifdef ALPHAMAP_1
            #ifdef DIFFUSEMAP_4
              #ifdef BLENDWITHALPHA
                alphaBlend1.r = calculateAlphaBlend(alphaBlend1.r, tex4.a);
              #endif
              diffuseColor = mix( diffuseColor, tex4, alphaBlend1.r );
              #ifdef DIFFUSEMAP_5
                #ifdef BLENDWITHALPHA
                  alphaBlend1.g = calculateAlphaBlend(alphaBlend1.g, tex5.a);
                #endif
                diffuseColor = mix( diffuseColor, tex5, alphaBlend1.g );
                #ifdef DIFFUSEMAP_6
                  #ifdef BLENDWITHALPHA
                    alphaBlend1.b = calculateAlphaBlend(alphaBlend1.b, tex6.a);
                  #endif
                  diffuseColor = mix( diffuseColor, tex6, alphaBlend1.b );
                  #ifdef DIFFUSEMAP_7
                    #ifdef BLENDWITHALPHA
                      alphaBlend1.a = calculateAlphaBlend(alphaBlend1.a, tex7.a);
                    #endif
                    diffuseColor = mix( diffuseColor, tex7, alphaBlend1.a );
                    #ifdef ALPHAMAP_2
                      #ifdef DIFFUSEMAP_8
                        #ifdef BLENDWITHALPHA
                          alphaBlend2.r = calculateAlphaBlend(alphaBlend2.r, tex8.a);
                        #endif
                        diffuseColor = mix( diffuseColor, tex8, alphaBlend2.r );
                        #ifdef DIFFUSEMAP_9
                          #ifdef BLENDWITHALPHA
                            alphaBlend2.g = calculateAlphaBlend(alphaBlend2.g, tex9.a);
                          #endif
                          diffuseColor = mix( diffuseColor, tex9, alphaBlend2.g );
                          #ifdef DIFFUSEMAP_10
                            #ifdef BLENDWITHALPHA
                              alphaBlend2.b = calculateAlphaBlend(alphaBlend2.b, tex10.a);
                            #endif
                            diffuseColor = mix( diffuseColor, tex10, alphaBlend2.b );
                            #ifdef DIFFUSEMAP_11
                              #ifdef BLENDWITHALPHA
                                alphaBlend2.a = calculateAlphaBlend(alphaBlend2.a, tex11.a);
                              #endif
                              diffuseColor = mix( diffuseColor, tex11, alphaBlend2.a );
                            #endif
                          #endif
                        #endif
                      #endif
                    #endif
                  #endif
                #endif
              #endif
            #endif
          #endif
        #endif
      #endif
    #endif

    return diffuseColor;
}

vec3 calculateNormalTriPlanar(in vec3 wNorm, in vec4 wVert,in vec2 texCoord) {
  // tri-planar texture bending factor for this fragment's world-space normal
  vec3 blending = abs( wNorm );
  blending = (blending -0.2) * 0.7;
  blending = normalize(max(blending, 0.00001));      // Force weights to sum to 1.0 (very important!)
  float b = (blending.x + blending.y + blending.z);
  blending /= vec3(b, b, b);

  // texture coords
  vec4 coords = wVert;
  vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );

  #ifdef ALPHAMAP_1
    vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
  #endif
  #ifdef ALPHAMAP_2
    vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
  #endif

  vec3 normal = vec3(0.5,0.5,1.0);
  vec3 n = vec3(0,0,0);

  
  #if defined(DIFFUSEMAP)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend.r = calculateAlphaBlend(alphaBlend.r, getTriPlanarBlend(coords, blending, m_DiffuseMap, m_DiffuseMap_0_scale).a);
  #endif
  #ifdef NORMALMAP
    n = getTriPlanarBlend(coords, blending, m_NormalMap, m_DiffuseMap_0_scale).xyz;
    normal = mix(normal, n, alphaBlend.r);
  #endif

  #if defined(DIFFUSEMAP_1)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend.g = calculateAlphaBlend(alphaBlend.g, getTriPlanarBlend(coords, blending, m_DiffuseMap_1, m_DiffuseMap_1_scale).a);
  #endif
  #ifdef NORMALMAP_1
    n = getTriPlanarBlend(coords, blending, m_NormalMap_1, m_DiffuseMap_1_scale).xyz;
    normal = mix(normal, n, alphaBlend.g);
  #else
    normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend.g);
  #endif

  #if defined(DIFFUSEMAP_2)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend.b = calculateAlphaBlend(alphaBlend.b, getTriPlanarBlend(coords, blending, m_DiffuseMap_2, m_DiffuseMap_2_scale).a);
  #endif
  #ifdef NORMALMAP_2
    n = getTriPlanarBlend(coords, blending, m_NormalMap_2, m_DiffuseMap_2_scale).xyz;
    normal = mix(normal, n, alphaBlend.b);
  #else
    normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend.b);
  #endif

  #if defined(DIFFUSEMAP_3)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend.a = calculateAlphaBlend(alphaBlend.a, getTriPlanarBlend(coords, blending, m_DiffuseMap_3, m_DiffuseMap_3_scale).a);
  #endif
  #ifdef NORMALMAP_3
    n = getTriPlanarBlend(coords, blending, m_NormalMap_3, m_DiffuseMap_3_scale).xyz;
    normal = mix(normal, n, alphaBlend.a);
  #else
    normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend.a);
  #endif

  #ifdef ALPHAMAP_1
    #if defined(DIFFUSEMAP_4)&amp;&amp; defined(BLENDWITHALPHA)
      alphaBlend1.r = calculateAlphaBlend(alphaBlend1.r, getTriPlanarBlend(coords, blending, m_DiffuseMap_4, m_DiffuseMap_4_scale).a);
    #endif
    #ifdef NORMALMAP_4
      n = getTriPlanarBlend(coords, blending, m_NormalMap_4, m_DiffuseMap_4_scale).xyz;
      normal = mix(normal, n, alphaBlend1.r);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend1.r);
    #endif

    #if defined(DIFFUSEMAP_5)&amp;&amp; defined(BLENDWITHALPHA)
      alphaBlend1.g = calculateAlphaBlend(alphaBlend1.g, getTriPlanarBlend(coords, blending, m_DiffuseMap_5, m_DiffuseMap_5_scale).a);
    #endif
    #ifdef NORMALMAP_5
      n = getTriPlanarBlend(coords, blending, m_NormalMap_5, m_DiffuseMap_5_scale).xyz;
      normal = mix(normal, n, alphaBlend1.g);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend1.g);
    #endif

    #if defined(DIFFUSEMAP_6)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend1.b = calculateAlphaBlend(alphaBlend1.b, getTriPlanarBlend(coords, blending, m_DiffuseMap_6, m_DiffuseMap_6_scale).a);
    #endif
    #ifdef NORMALMAP_6
      n = getTriPlanarBlend(coords, blending, m_NormalMap_6, m_DiffuseMap_6_scale).xyz;
      normal = mix(normal, n, alphaBlend1.b);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend1.b);
    #endif

    #if defined(DIFFUSEMAP_7)&amp;&amp; defined(BLENDWITHALPHA)
      alphaBlend1.a = calculateAlphaBlend(alphaBlend1.a, getTriPlanarBlend(coords, blending, m_DiffuseMap_7, m_DiffuseMap_7_scale).a);
    #endif
    #ifdef NORMALMAP_7
      n = getTriPlanarBlend(coords, blending, m_NormalMap_7, m_DiffuseMap_7_scale).xyz;
      normal = mix(normal, n, alphaBlend1.a);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend1.a);
    #endif
  #endif

  #ifdef ALPHAMAP_2

    #if defined(DIFFUSEMAP_8)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend2.r = calculateAlphaBlend(alphaBlend2.r, getTriPlanarBlend(coords, blending, m_DiffuseMap_8, m_DiffuseMap_8_scale).a);
    #endif
    #ifdef NORMALMAP_8
      n = getTriPlanarBlend(coords, blending, m_NormalMap_8, m_DiffuseMap_8_scale).xyz;
      normal = mix(normal, n, alphaBlend2.r);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend2.r);
    #endif

    #if defined(DIFFUSEMAP_9)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend2.g = calculateAlphaBlend(alphaBlend2.g, getTriPlanarBlend(coords, blending, m_DiffuseMap_9, m_DiffuseMap_9_scale).a);
    #endif
    #ifdef NORMALMAP_9
      n = getTriPlanarBlend(coords, blending, m_NormalMap_9, m_DiffuseMap_9_scale).xyz;
      normal = mix(normal, n, alphaBlend2.g);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend2.g);
    #endif

    #if defined(DIFFUSEMAP_10)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend2.b = calculateAlphaBlend(alphaBlend2.b, getTriPlanarBlend(coords, blending, m_DiffuseMap_10, m_DiffuseMap_10_scale).a);
    #endif
    #ifdef NORMALMAP_10
      n = getTriPlanarBlend(coords, blending, m_NormalMap_10, m_DiffuseMap_10_scale).xyz;
      normal = mix(normal, n, alphaBlend2.b);
      #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend2.b);
    #endif

    #if defined(DIFFUSEMAP_11)&amp;&amp; defined(BLENDWITHALPHA)
    alphaBlend2.a = calculateAlphaBlend(alphaBlend2.a, getTriPlanarBlend(coords, blending, m_DiffuseMap_11, m_DiffuseMap_11_scale).a);
    #endif
    #ifdef NORMALMAP_11
      n = getTriPlanarBlend(coords, blending, m_NormalMap_11, m_DiffuseMap_11_scale).xyz;
      normal = mix(normal, n, alphaBlend2.a);
    #else
      normal = mix(normal, vec3(0.5,0.5,1.0), alphaBlend2.a);
    #endif
  #endif

  return normalize(normal.xyz * vec3(2.0) - vec3(1.0));
}

#endif

#endif

#if defined(PARALLAXMAPPING)&& defined(BLENDWITHALPHA)
float sampleHeight(in vec2 texCoord){

vec4 alphaBlend   = texture2D( m_AlphaMap, texCoord.xy );
#ifdef ALPHAMAP_1
  vec4 alphaBlend1   = texture2D( m_AlphaMap_1, texCoord.xy );
#endif
#ifdef ALPHAMAP_2
  vec4 alphaBlend2   = texture2D( m_AlphaMap_2, texCoord.xy );
#endif

height = 0.0;
float alphaMap= texture2D(m_DiffuseMap, texCoord * m_DiffuseMap_0_scale).a;
float alpha = alphaMap*calculateAlphaBlend(alphaBlend.r, alphaMap);

#ifdef DIFFUSEMAP_1
 alphaMap = texture2D(m_DiffuseMap_1, texCoord * m_DiffuseMap_1_scale).a;
 alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend.g, alphaMap));
  #ifdef DIFFUSEMAP_2
  alphaMap = texture2D(m_DiffuseMap_2, texCoord * m_DiffuseMap_2_scale).a;
  alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend.b, alphaMap));
    #ifdef DIFFUSEMAP_3
      alphaMap = texture2D(m_DiffuseMap_3, texCoord * m_DiffuseMap_3_scale).a;
      alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend.a, alphaMap));
      #ifdef DIFFUSEMAP_4
        alphaMap = texture2D(m_DiffuseMap_4, texCoord * m_DiffuseMap_4_scale).a;
        alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend1.r, alphaMap));
        #ifdef DIFFUSEMAP_5
          alphaMap = texture2D(m_DiffuseMap_5, texCoord * m_DiffuseMap_5_scale).a;
          alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend1.g, alphaMap));
          #ifdef DIFFUSEMAP_6
            alphaMap = texture2D(m_DiffuseMap_6, texCoord * m_DiffuseMap_6_scale).a;
            alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend1.b, alphaMap));
            #ifdef DIFFUSEMAP_7
              alphaMap = texture2D(m_DiffuseMap_7, texCoord * m_DiffuseMap_7_scale).a;
              alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend1.a, alphaMap));
              #ifdef DIFFUSEMAP_8
                alphaMap = texture2D(m_DiffuseMap_8, texCoord * m_DiffuseMap_8_scale).a;
                alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend2.r, alphaMap));
                #ifdef DIFFUSEMAP_9
                  alphaMap = texture2D(m_DiffuseMap_9, texCoord * m_DiffuseMap_9_scale).a;
                  alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend2.g, alphaMap));
                  #ifdef DIFFUSEMAP_10
                    alphaMap = texture2D(m_DiffuseMap_10, texCoord * m_DiffuseMap_10_scale).a;
                    alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend2.b, alphaMap));
                    #ifdef DIFFUSEMAP_11
                      alphaMap = texture2D(m_DiffuseMap_11, texCoord * m_DiffuseMap_11_scale).a;
                      alpha = mix( alpha, alphaMap, calculateAlphaBlend(alphaBlend2.a, alphaMap));
                    #endif
                  #endif
                #endif
              #endif
            #endif
          #endif
        #endif
      #endif
    #endif
  #endif
#endif

return min(alpha*1.2,1.0);

}
#ifdef STEEPPARALLAX
vec2 steepParallaxOffset(vec3 vViewDir,vec2 texCoord){//,float parallaxScale){
// Test if the first value found is already at the highest value;
float fCurrHeight = sampleHeight(texCoord);
if (fCurrHeight > 1.0){
return texCoord;
}
vViewDir.y *= -1.0;

    vec2 vParallaxDirection = normalize(  vViewDir.xy );

    // The length of this vector determines the furthest amount of displacement: (Ati's comment)
    float fLength         = length( vViewDir );
    float fParallaxLength = sqrt( fLength * fLength - vViewDir.z * vViewDir.z ) / vViewDir.z; 

    // Compute the actual reverse parallax displacement vector: (Ati's comment)
    vec2 vParallaxOffsetTS = vParallaxDirection * fParallaxLength;

    // Need to scale the amount of displacement to account for different height ranges
    // in height maps. This is controlled by an artist-editable parameter: (Ati's comment)              
    //parallaxScale *=0.3;
    vParallaxOffsetTS *= m_ParallaxHeight;

    vec3 eyeDir = normalize(vViewDir).xyz;   

    float nMinSamples = 1.0;
    float nMaxSamples = m_ParallaxSamples; //* parallaxScale;   
    float nNumSamples = mix( nMinSamples, nMaxSamples, 1.0 - eyeDir.z );   //In reference shader: int nNumSamples = (int)(lerp( nMinSamples, nMaxSamples, dot( eyeDirWS, N ) ));
    float fStepSize = 1.0 / nNumSamples;   
    float fPrevHeight = 1.0;
    float fNextHeight = 0.0;
    float nStepIndex = 0.0;
    vec2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
    vec2 vTexCurrentOffset = texCoord;
    float  fCurrentBound     = 1.0;
    float  fParallaxAmount   = 0.0; 
    while ( nStepIndex &lt; nNumSamples &amp;&amp; fCurrHeight &lt; fCurrentBound ) {
        vTexCurrentOffset -= vTexOffsetPerStep;
        fPrevHeight = fCurrHeight;
           // This line is changed specific for the terrain shader.
           fCurrHeight = sampleHeight(vTexCurrentOffset); 

        fCurrentBound -= fStepSize;
        nStepIndex+=1.0;

    }
    vec2 pt1 = vec2( fCurrentBound, fCurrHeight );
    vec2 pt2 = vec2( fCurrentBound + fStepSize, fPrevHeight );

    float fDelta2 = pt2.x - pt2.y;
    float fDelta1 = pt1.x - pt1.y;

    float fDenominator = fDelta2 - fDelta1;

    fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;

    vec2 vParallaxOffset = vParallaxOffsetTS * (1.0 - fParallaxAmount );
   return texCoord - vParallaxOffset;  
}

#else
vec2 classicParallaxOffset(vec3 vViewDir,vec2 texCoord){
float h=sampleHeight(texCoord);
float heightScale = m_ParallaxHeight;
float heightBias = heightScale* -0.6;
vViewDir.y *= -1.0;
vec3 normView = normalize(vViewDir);
h = (h * heightScale + heightBias) * normView.z;
return texCoord + (h * normView.xy);
}
#endif
#endif

void main(){

//----------------------
// diffuse calculations
//----------------------
#ifdef DIFFUSEMAP
  #ifdef ALPHAMAP
    #ifdef TRI_PLANAR_MAPPING
        vec4 diffuseColor = calculateTriPlanarDiffuseBlend(wNormal, wVertex, texCoord);
    #else
        #if defined(PARALLAXMAPPING)&amp;&amp; defined(BLENDWITHALPHA)  
          #ifdef STEEPPARALLAX
            texCoord = steepParallaxOffset(vViewDir, texCoord);
          #else
            texCoord = classicParallaxOffset(vViewDir, texCoord);
          #endif
        #endif
        vec4 diffuseColor = calculateDiffuseBlend(texCoord);
    #endif
  #else
    vec4 diffuseColor = texture2D(m_DiffuseMap, texCoord);
  #endif
#else
  vec4 diffuseColor = vec4(1.0);
#endif

    float spotFallOff = 1.0;
    if(g_LightDirection.w!=0.0){
          vec3 L=normalize(lightVec.xyz);
          vec3 spotdir = normalize(g_LightDirection.xyz);
          float curAngleCos = dot(-L, spotdir);             
          float innerAngleCos = floor(g_LightDirection.w) * 0.001;
          float outerAngleCos = fract(g_LightDirection.w);
          float innerMinusOuter = innerAngleCos - outerAngleCos;

          spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;

          if(spotFallOff &lt;= 0.0){
              gl_FragColor = AmbientSum * diffuseColor;
              return;
          }else{
              spotFallOff = clamp(spotFallOff, 0.0, 1.0);
          }
    }

//---------------------
// normal calculations
//---------------------
#if defined(NORMALMAP) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(NORMALMAP_8) || defined(NORMALMAP_9) || defined(NORMALMAP_10) || defined(NORMALMAP_11)
  #ifdef TRI_PLANAR_MAPPING
      vec3 normal = calculateNormalTriPlanar(wNormal, wVertex, texCoord);
  #else
    vec3 normal = calculateNormal(texCoord);
  #endif
#else
  vec3 normal = vNormal;
#endif


//-----------------------
// lighting calculations
//-----------------------
vec4 lightDir = vLightDir;
lightDir.xyz = normalize(lightDir.xyz);

vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz)*spotFallOff;

vec4 specularColor = vec4(1.0);

//--------------------------
// final color calculations
//--------------------------
gl_FragColor =  AmbientSum * diffuseColor +
                DiffuseSum * diffuseColor  * light.x +
                SpecularSum * specularColor * light.y;

//gl_FragColor.a = alpha;

}
[/java]

While working on this I noticed two bugs the first one was caused by using a normal map for one terrain texture and the combined normal map wasn’t being normalized correctly.
Bugged Screenshot:

Fixed Screenshot:

The other one was with ParallaxMapping the depth looked like it was shifted in the wrong direction (Screenshots were taken from the parallax mapping sample in the default jMonkey tests)
I needed to flip vViewDir: “vViewDir.y *= -1.0;” but I’m not sure why I needed to do this (I mean it fits the way the bug looks, but seems still kind of odd to me).
Bugged Screenshot:

Fixed Screenshot:

PS: The terrain shader is one ugly monstrosity there is “#ifdef” everywhere and if you change something you have to copy & paste it to a thousand places.

And some Pictures of the whole thing in action.

12 Likes

:smiley:

Wonderful work!!!

Cool, I’ve been planning on adding in this feature, but it looks like you have it now. I will look at the changes and see about integrating them in.

Yes it is a monster shader, but the #ifdefs are pretty common in the pre-shaderNode jme shaders. It saves on using actual control structures that can be fatal on some older cards.

2 Likes

I had the same problem with the parallax! Thanks for fixing it! And the awesome share :slight_smile:

@Perjin is this issue only with terrain? Does Lighting have it?
Also about the bloat of the shader, it’s the prize to pay to have a shader that does everything :p. That’s also a reason why shader nodes where made, I need to make all the stock shaders base on shader nodes.

Thanks for the contribution that’s brilliant.

@Sploreg Awesome I really hoped this would make it into the engine.

@Setekh Glad I could help. I was worried that it was problem on my end, since it wasn’t mentioned anywhere and made the parallax pretty much unusable.

@nehon if you mean the normal map issue yes thats only in the terrain shader, it’s happening as soon as a normal map is used because the calculation is just wrong.
It is doing this for each layer (if there is a normal map for the layer it uses the value found in the texture instead of the vec3):
[java]
normal += vec3(0.5,0.5,1) * alphaBlend.b;[/java]

And tries to normalize it with the following line at the end:
[java]
normal = (normal.xyz * vec3(2.0) - vec3(1.0));
return normalize(normal);[/java]

This approach might kind of work, if the layers are normalized summing up to 1.0 in total value, but the terrain shader never do this.

I’m now using the mix function for each layer, this way the resulting vector will be neutral:
[java]normal = mix (normal, vec3(0.5,0.5,1.0), alphaBlend1.r);[/java]

After that it is normalized the same way as before.

3 Likes

Wow! Yet another awesome terrain improvement. I can’t wait to see how this looks with @jayfella 's paging and trees, although I guess I should wait until @sploreg integrates it properly before going there :slight_smile:

@Perjin Have you thought about LOD and detail texturing at all? For example, in the far distance, the normal maps and parallax mapping are not really needed, and a single base texture can cover the whole terrain so we don’t see repetition, whereas in close, we need all the bumps and details and some high-res textures which need much smaller scale.

@monkeychops I didn’t think about LOD but it should be easy, I’d just modify the parallax height and samples count with the fragment depth and eventually don’t do the parallax at all, same thing for the normal map.

Is this currently in the engine? I couldn’t find it.

No it isn’t in the engine right now. Since jmonkey moved to git I’ll just create a pull request for this, then it can be added (I’ll also add the necessary changes in the terrain editor).

2 Likes

Awesome! Thanks!

I created a patch with everything needed for the shader, with an updated terrain editor.
I rewrote most of the code and tested it on nvidia and an integrated intel graphic card. (It is now a lot faster than the code above).
I removed the steep parallax mapping because i couldn’t get it to work properly with sloped terrain it would always look very distorted on stretched textures.

But I won’t create a pull request for this, because I think it would be better if we had a system to switch between different shaders for the terrain in the editor.
There are multiple reasons for this:

  • First the terrain shader gets incredibly bloated and with it unmanagable.

  • Second, the height based method and the normal terrain painting are barely compatible, they use the same inputs but you can’t just switch between them, the resulting terrain will look very different whenever you are switching.

  • Third, you need specific textures (height information in the alpha channel) for the height based terrain splatting.

(Shader nodes would probably help with the bloat, but I still think it would be better if shaders could be switched in the terrain editor.)

1 Like

Without looking into it can’t you just make a new terrain material and apply in the terrain editor and that would use other shaders?

You can change the material, but you have to do it for each terrain-quad-patch, the material settings in the terrain editor (add/remove layers, tri planar mapping etc.) won’t have an effect on your custom material and whenever you reopen the terrain editor again it’ll switch back to the default terrain shader.

Thank you!
It’d be awesome if this was integrated as a stock functionality.

I think this could be integrated into jme 3.1. The terrain editor needs the ability to switch between shaders. The main requirement of the shader is to be able to support multiple texture layers, and having the parameters named similarly across shaders. If this shader supports the same number of texture layers then it could be integrated fairly easily.
In the future I would like the editor to support much different terrain shaders that allow for many more texture layers (using texture arrays etc).
But for now a simple drop down in the terrain editor panel that specifies what shader to use, and copies the parameters over when switched, would be a good solution.

Are you up for adding that in? I honestly just don’t have the time this fall.

1 Like

I really would like to get a hand on this, it s exactly what i was looking for to improve the graphics of my game :open_mouth:
It seem to be a old topic but I did not found the material definition inside the jme3 :frowning:

Anyway good work!