Parallax Occlusion Mapping for jme

This replaces Steep Parallax and should give better quality with fewer samples

Implementation details are here: LearnOpenGL - Parallax Mapping

11 Likes

sorry i thought there is missing import.

but i just noticed you use import in 91 line.

@RiccardoBlb any chance you can share a demo screenshot from a scene with Occlusion Parallax enabled and one with Occlusion Parallax disabled.

not good example, have wrong texture to show and sphere is not full round, but maybe some details might be visible.

will try implement this into terrain PBR now.

vvvvv1
vvvvv2

2 Likes

@Ali_RS

here for you my friend, much better preview.

its visible it really make a lot difference, its much better.

Riccardo made a great work adding this PR into JME.

edit:

and even better example:

im not sure why color dont work for me, but probably i meesed something up.

now just need wait for @yaRnMcDonuts finished JME PBR Terrain to also add this occlusion parallax for terrain textures. really Terrain PBR for jme is missing feature too.

2 Likes

Given enought samples, steep parallax should look the same as parallax occlusion mapping, this should only reduce the number of samples required to get the same visual effect.

2 Likes

@oxplay2 thanks for the examples. :slightly_smiling_face:

1 Like

i think i am successfull adapt it into PBR Terrain.

here result:

im still not sure if everything is ok, but looks fine so far. if @yaRnMcDonuts will need to know how i added it there i can tell so he could prepare JME proper version.

im not sure if @RiccardoBlb used JME terrain on his VIP video, it might be just Quad and object mountains, but anyway im glad it work on PBR Terrain. again great work Riccardo!

edit:

hehe, even water with just moving tex coords look nice, just add some noise blend for parallax texcoords and there would be nice water.

5 Likes

made some another experiment.

seen this parallax occlusion take down some FPS, so why not optimize it with some LOD.
so made some camera distance condition: if(camDist < 0.1){
i know conditions are slow in shaders, but seen no other way for this.

but it dont seem to save a lot fps, it save only if far away, as seen on video.

edit, updated video:

any clues what can be done to LOD-optimize it?

1 Like

i haven’t looked at this project, but you use a step function to avoid an if statement. Sometimes you just can’t get away from using one.

It has to evaluate both outcomes when both near and far are in the scene, so you save nothing, it just looks worse. In fact it might even be slower now because it does 2 operations. Pretty much the same as what a step mix would do.

I don’t think you can LOD like you want it to because you can’t give the result unless you have the value, so when you call a mix, even if it’s a 1 or a zero it still has to know the result to mix.

1 Like

i haven’t looked at this project, but you use a step function to avoid an if statement.

thanks, what is this “step function”?
i seen i can use ifdef (or if defined()) to not use heavy if conditions. also learned about this ##index from terrain shader. but i dont know what is this “step function”.

hmm i saved 30 fps with double distance(so better effect) and one thing: each terrain texture (3 there) if defined are causing parallax method to execute (if parallax is defined for this texture ofc)

its like:

#ifdef ALBEDOMAP_0   
    DEFINE_COORD(_0)
    #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_0)
        PARALLAX_BLEND(_0, alphaBlend.r)
    #endif
    #ifdef NORMALMAP_0
        BLEND_NORMAL(_0,  alphaBlend.r)
    #else
        BLEND(_0,  alphaBlend.r)
    #endif
#endif

so i just added if(intensity > 0.1){ so it skip heavy code from executing if not blended more than 0.1f;

it saved 30 fps like for me for this 3 textures, i think it will save a much more if there would be like 11 textures…

Those are called preprocessors. They are executed before the shader is compiled and truncate the result. If the texture is not defined, just remove all the code inside the ifdef, for example. So if no normal map is defined, don’t even bother calculating it. It’s great for things like that.

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/step.xhtml

A step function returns a 1 or 0 based on whether a < b or whatever. You can then use it for a mix for example instead of an if statement.

1 Like

its a very nice function.

but i dont know how to use it to skip heavy parallax code, i would need if condition anyway.

when i would need to use it on calculated parallax value, it will anyway be already calculated by heavy code.

what i mean i can do float isInLOD = step(camDist, m_ParallaxLODDistance);

but anyway i need condition.

You could have 2 materials, one with a map and one without. Just give the materials closest to the camera the parallax and normal maps and the ones further away none of them. Its a common case. If you play ark you can even see it swapping low res textures at like 10m away. That game has some crazy lod techniques.

1 Like

Only for divergent branches. It will work fine in this case.
It should also be possible to reduce the height by the distance, so that the transition is smooth and you don’t get an abrupt interruption.

2 Likes

You could have 2 materials, one with a map and one without. Just give the materials closest to the camera the parallax and normal maps and the ones further away none of them. Its a common case. If you play ark you can even see it swapping low res textures at like 10m away. That game has some crazy lod techniques.

would need to see code example.

Problem is that Terrain is One material (it share it for all tiles)

i can only imagine stupid code that anyway got condition like:

float isInLOD = step(camDist < m_ParallaxLODDistance);
if(isInLOD == 1) {
#import “highDetailFragmentShader”
} else {
#import “lowDetailFragmentShader”
}

but its 100% not what you mean.

rather based on my knowledge i would make it just via game “setMaterial” for entites far / close distance based.

But anyway Terrain is just one material all the time. so it dont help for this case.

Right i could switch Tile materials somehow (but they have shared material). But not sure it it would not break terrain or terrain itself will not override it later.

Anyway i feel like i dont get your idea, because i never seen solutions like this. (using this step function)

But, i think i could try override Terrain system to use different material for far tiles if possible.
Problem is, i seen Terrain anyway got 9 tiles right? so if we go from center tile to left tile and we are on seam between tiles, there would be seam because one tile use parallax and other not.

here is the Terrain PBR/parallax occlusion based on mix of @yaRnMcDonuts work and @RiccardoBlb work.

yaRnMcDonuts source:

it dont tave tri plannar, because im not sure how to manage texCoords there, but someone might add it.

!!! Please replace “MatDefs” with “Common” for imports, or use same dirs in project as me. I use MatDefs folder with cloned libs to be able to see shader in 3.2.2 IDE properly.

Also there is:
**coord_2.x += g_Time * 0.03; **
to demonstrate tex moving like water, remove it or use if need.

And also: you need ShaderLib/OcclusionParallax.glsllib from Riccardo PR

NORMALMAP_PARALLAX - this is not used, would just need to add to j3md and use

calculateParallax(coord##index, m_ParallaxHeight##index, m_NormalMap##index, ab);

instead of

calculateParallax(coord##index, m_ParallaxHeight##index, m_ParallaxMap##index, ab);

when ifdef NORMALMAP_PARALLAX is true.

frag:

#import "MatDefs/ShaderLib/GLSLCompat.glsllib"
#import "MatDefs/ShaderLib/PBR.glsllib"
#import "MatDefs/ShaderLib/Parallax.glsllib"
#import "MatDefs/ShaderLib/Lighting.glsllib"

uniform vec4 g_LightData[NB_LIGHTS];

uniform vec4 g_AmbientLightColor;

varying vec3 wPosition;

uniform float g_Time;

// Configure height channel
#ifdef NORMALMAP_PARALLAX
  #define HEIGHT_MAP A_COMPONENT
#else
  #define HEIGHT_MAP R_COMPONENT
#endif
#import "MatDefs/ShaderLib/OcclusionParallax.glsllib"

#if NB_PROBES >= 1
  uniform samplerCube g_PrefEnvMap;
  uniform vec3 g_ShCoeffs[9];
  uniform mat4 g_LightProbeData;
#endif
#if NB_PROBES >= 2
  uniform samplerCube g_PrefEnvMap2;
  uniform vec3 g_ShCoeffs2[9];
  uniform mat4 g_LightProbeData2;
#endif
#if NB_PROBES == 3
  uniform samplerCube g_PrefEnvMap3;
  uniform vec3 g_ShCoeffs3[9];
  uniform mat4 g_LightProbeData3;
#endif

#ifdef EMISSIVE
    uniform vec4 m_Emissive;
#endif
#ifdef EMISSIVEMAP
    uniform sampler2D m_EmissiveMap;
#endif
#if defined(EMISSIVE) || defined(EMISSIVEMAP)
    uniform float m_EmissivePower;
    uniform float m_EmissiveIntensity;
#endif 

#ifdef SPECGLOSSPIPELINE
  uniform sampler2D m_SpecularMap;
  uniform sampler2D m_GlossMap;
#endif

#ifdef LIGHTMAP
  uniform sampler2D m_LightMap;
#endif

varying vec3 vNormal;

#if defined(NORMALMAP_0) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(PLAGUEDNORMALMAP)
    varying vec4 wTangent;
#endif

varying vec2 texCoord;
uniform vec3 inPosition;
uniform mat4 g_WorldViewMatrix;
uniform vec3 g_CameraPosition;

varying vec3 vPosition;
varying vec3 vnPosition;
varying vec3 vViewDir;
varying vec4 vLightDir;
varying vec4 vnLightDir;
varying vec3 lightVec;

uniform float m_Roughness_0;
uniform float m_Roughness_1;
uniform float m_Roughness_2;
uniform float m_Roughness_3;
uniform float m_Roughness_4;
uniform float m_Roughness_5;
uniform float m_Roughness_6;
uniform float m_Roughness_7;
uniform float m_Roughness_8;
uniform float m_Roughness_9;
uniform float m_Roughness_10;
uniform float m_Roughness_11;

uniform float m_Metallic_0;
uniform float m_Metallic_1;
uniform float m_Metallic_2;
uniform float m_Metallic_3;
uniform float m_Metallic_4;
uniform float m_Metallic_5;
uniform float m_Metallic_6;
uniform float m_Metallic_7;
uniform float m_Metallic_8;
uniform float m_Metallic_9;
uniform float m_Metallic_10;
uniform float m_Metallic_11;

uniform float m_Parallax_0;
uniform float m_Parallax_1;
uniform float m_Parallax_2;
uniform float m_Parallax_3;
uniform float m_Parallax_4;
uniform float m_Parallax_5;
uniform float m_Parallax_6;
uniform float m_Parallax_7;
uniform float m_Parallax_8;
uniform float m_Parallax_9;
uniform float m_Parallax_10;
uniform float m_Parallax_11;

#ifdef PARALLAX_LOD_DISTANCE
    uniform float m_ParallaxLODDistance;
#endif

#ifdef ALBEDOMAP_0
  uniform sampler2D m_AlbedoMap_0;
#endif
#ifdef ALBEDOMAP_1
  uniform sampler2D m_AlbedoMap_1;
#endif
#ifdef ALBEDOMAP_2
  uniform sampler2D m_AlbedoMap_2;
#endif
#ifdef ALBEDOMAP_3
  uniform sampler2D m_AlbedoMap_3;
#endif
#ifdef ALBEDOMAP_4
  uniform sampler2D m_AlbedoMap_4;
#endif
#ifdef ALBEDOMAP_5
  uniform sampler2D m_AlbedoMap_5;
#endif
#ifdef ALBEDOMAP_6
  uniform sampler2D m_AlbedoMap_6;
#endif
#ifdef ALBEDOMAP_7
  uniform sampler2D m_AlbedoMap_7;
#endif
#ifdef ALBEDOMAP_8
  uniform sampler2D m_AlbedoMap_8;
#endif
#ifdef ALBEDOMAP_9
  uniform sampler2D m_AlbedoMap_9;
#endif
#ifdef ALBEDOMAP_10
  uniform sampler2D m_AlbedoMap_10;
#endif
#ifdef ALBEDOMAP_11
  uniform sampler2D m_AlbedoMap_11;
#endif


#ifdef ALBEDOMAP_0
  uniform float m_AlbedoMap_0_scale;
#endif
#ifdef ALBEDOMAP_1
  uniform float m_AlbedoMap_1_scale;
#endif
#ifdef ALBEDOMAP_2
  uniform float m_AlbedoMap_2_scale;
#endif
#ifdef ALBEDOMAP_3
  uniform float m_AlbedoMap_3_scale;
#endif
#ifdef ALBEDOMAP_4
  uniform float m_AlbedoMap_4_scale;
#endif
#ifdef ALBEDOMAP_5
  uniform float m_AlbedoMap_5_scale;
#endif
#ifdef ALBEDOMAP_6
  uniform float m_AlbedoMap_6_scale;
#endif
#ifdef ALBEDOMAP_7
  uniform float m_AlbedoMap_7_scale;
#endif
#ifdef ALBEDOMAP_8
  uniform float m_AlbedoMap_8_scale;
#endif
#ifdef ALBEDOMAP_9
  uniform float m_AlbedoMap_9_scale;
#endif
#ifdef ALBEDOMAP_10
  uniform float m_AlbedoMap_10_scale;
#endif
#ifdef ALBEDOMAP_11
  uniform float m_AlbedoMap_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_0
  uniform sampler2D m_NormalMap_0;
#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 PARALLAXMAP_0
  uniform sampler2D m_ParallaxMap_0;
  uniform float m_ParallaxHeight_0;
#endif
#ifdef PARALLAXMAP_1
  uniform sampler2D m_ParallaxMap_1;
  uniform float m_ParallaxHeight_1;
#endif
#ifdef PARALLAXMAP_2
  uniform sampler2D m_ParallaxMap_2;
  uniform float m_ParallaxHeight_2;
#endif
#ifdef PARALLAXMAP_3
  uniform sampler2D m_ParallaxMap_3;
  uniform float m_ParallaxHeight_3;
#endif
#ifdef PARALLAXMAP_4
  uniform sampler2D m_ParallaxMap_4;
  uniform float m_ParallaxHeight_4;
#endif
#ifdef PARALLAXMAP_5
  uniform sampler2D m_ParallaxMap_5;
  uniform float m_ParallaxHeight_5;
#endif
#ifdef PARALLAXMAP_6
  uniform sampler2D m_ParallaxMap_6;
  uniform float m_ParallaxHeight_6;
#endif
#ifdef PARALLAXMAP_7
  uniform sampler2D m_ParallaxMap_7;
  uniform float m_ParallaxHeight_7;
#endif
#ifdef PARALLAXMAP_8
  uniform sampler2D m_ParallaxMap_8;
  uniform float m_ParallaxHeight_8;
#endif
#ifdef PARALLAXMAP_9
  uniform sampler2D m_ParallaxMap_9;
  uniform float m_ParallaxHeight_9;
#endif
#ifdef PARALLAXMAP_10
  uniform sampler2D m_ParallaxMap_10;
  uniform float m_ParallaxHeight_10;
#endif
#ifdef PARALLAXMAP_11
  uniform sampler2D m_ParallaxMap_11;
  uniform float m_ParallaxHeight_11;
#endif


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

varying float camDist;

vec2 coord;
vec4 albedo;
vec4 tempAlbedo, tempNormal;
vec3 normal = vec3(0.5,0.5,1);
vec3 newNormal;
vec3 parallax;
vec2 newTexCoord;
vec3 viewDir;
mat3 tbnMat;
vec3 norm;
float Metallic;
float Roughness;

#define DEFINE_COORD(index) vec2 coord##index = newTexCoord * m_AlbedoMap##index##_scale;

#define BLEND(index, ab)\
    tempAlbedo.rgb = texture2D(m_AlbedoMap##index, coord##index).rgb;\
    albedo.rgb = mix( albedo.rgb, tempAlbedo.rgb ,ab );\
    normal.rgb = mix(normal.xyz, wNormal.xyz, ab);\
    Metallic = mix(Metallic, m_Metallic##index, ab);\
    Roughness = mix(Roughness, m_Roughness##index, ab);

#define BLEND_NORMAL(index, ab)\
    tempAlbedo.rgb = texture2D(m_AlbedoMap##index, coord##index).rgb;\
    albedo.rgb = mix( albedo.rgb, tempAlbedo.rgb ,ab );\
    Metallic = mix(Metallic, m_Metallic##index, ab);\
    Roughness = mix(Roughness, m_Roughness##index, ab);\
    newNormal = texture2D(m_NormalMap##index, coord##index).xyz;\
    normal = mix(normal, newNormal, ab);

#define PARALLAX_BLEND(index, ab)\
    calculateParallax(coord##index, m_ParallaxHeight##index, m_ParallaxMap##index, ab);

void calculateParallax(inout vec2 newTexCoord, in float parallaxHeight, in sampler2D map, in float intensity) {
    #ifdef PARALLAX_LOD_DISTANCE  
        if(camDist < m_ParallaxLODDistance && intensity > 0.1){
            vec3 vViewDir =  viewDir * tbnMat;
            Parallax_initFor(vViewDir,parallaxHeight);
            Parallax_displaceCoords(newTexCoord,map);
        }
    #else
        if(intensity > 0.1){
            vec3 vViewDir =  viewDir * tbnMat;
            Parallax_initFor(vViewDir,parallaxHeight);
            Parallax_displaceCoords(newTexCoord,map);
        }
    #endif
}

vec4 calculateAlbedoBlend(in vec2 newTexCoord) {
    vec4 alphaBlend = texture2D( m_AlphaMap, newTexCoord.xy );
    vec4 albedo = vec4(1.0);
    Roughness = m_Roughness_0;
    Metallic = m_Metallic_0 ;
    
    vec3 blending = abs( wNormal );
    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);

    vec3 wvPosition=(vec4(inPosition,1)*g_WorldViewMatrix).xyz;
    #ifdef ALPHAMAP_1
      vec4 alphaBlend1   = texture2D( m_AlphaMap_1, newTexCoord.xy );
    #endif
    #ifdef ALPHAMAP_2
      vec4 alphaBlend2   = texture2D( m_AlphaMap_2, newTexCoord.xy );
    #endif
    #ifdef ALBEDOMAP_0   
        DEFINE_COORD(_0)
        //NOTE! the old (phong) terrain shaders do not have an "_0" for the first diffuse map, it is just "DiffuseMap"
        #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_0)
            PARALLAX_BLEND(_0, alphaBlend.r)
        #endif
        #ifdef NORMALMAP_0
            BLEND_NORMAL(_0,  alphaBlend.r)
        #else
            BLEND(_0,  alphaBlend.r)
        #endif
    #endif
    #ifdef ALBEDOMAP_1
        DEFINE_COORD(_1)
        #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_1)
            PARALLAX_BLEND(_1, alphaBlend.g)
        #endif
        #ifdef NORMALMAP_1
            BLEND_NORMAL(_1,  alphaBlend.g)
        #else
            BLEND(_1,  alphaBlend.g)
        #endif
    #endif
    #ifdef ALBEDOMAP_2
        DEFINE_COORD(_2)
        #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_2)
            coord_2.x += g_Time * 0.03;
            PARALLAX_BLEND(_2, alphaBlend.b)
        #endif
        #ifdef NORMALMAP_2
            BLEND_NORMAL(_2,  alphaBlend.b)
        #else
            BLEND(_2,  alphaBlend.b)
        #endif
    #endif
    #ifdef ALBEDOMAP_3 
        DEFINE_COORD(_3)
        #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_3)
            PARALLAX_BLEND(_3, alphaBlend.a)
        #endif
        #ifdef NORMALMAP_3
            BLEND_NORMAL(_3,  alphaBlend.a)
        #else
            BLEND(_3,  alphaBlend.a)
        #endif
    #endif

    #ifdef ALPHAMAP_1
        #ifdef ALBEDOMAP_4
            DEFINE_COORD(_4)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_4)
                PARALLAX_BLEND(_4,  alphaBlend1.r)
            #endif
            #ifdef NORMALMAP_4
                BLEND_NORMAL(_4,  alphaBlend1.r)
            #else
                BLEND(_4,  alphaBlend1.r)
            #endif
        #endif
        #ifdef ALBEDOMAP_5
            DEFINE_COORD(_5)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_5)
                PARALLAX_BLEND(_5,  alphaBlend1.g)
            #endif
            #ifdef NORMALMAP_5
                BLEND_NORMAL(_5,  alphaBlend1.g)
            #else
                BLEND(_5,  alphaBlend1.g)
            #endif
        #endif
        #ifdef ALBEDOMAP_6
            DEFINE_COORD(_6)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_6)
                PARALLAX_BLEND(_6,  alphaBlend1.b)
            #endif
            #ifdef NORMALMAP_6
                BLEND_NORMAL(_6,  alphaBlend1.b)
            #else
                BLEND(_6,  alphaBlend1.b)
            #endif
        #endif
        #ifdef ALBEDOMAP_7
            DEFINE_COORD(_7)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_7)
                PARALLAX_BLEND(_7,  alphaBlend1.a)
            #endif
            #ifdef NORMALMAP_7
                BLEND_NORMAL(_7,  alphaBlend1.a)
            #else
                BLEND(_7,  alphaBlend1.a)
            #endif
        #endif
    #endif

    #ifdef ALPHAMAP_2
        #ifdef ALBEDOMAP_8
            DEFINE_COORD(_8)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_8)
                PARALLAX_BLEND(_8,  alphaBlend2.r)
            #endif
            #ifdef NORMALMAP_8
                BLEND_NORMAL(_8,  alphaBlend2.r)
            #else
                BLEND(_8,  alphaBlend2.r)
            #endif
        #endif
        #ifdef ALBEDOMAP_9
            DEFINE_COORD(_9)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_9)
                PARALLAX_BLEND(_9,  alphaBlend2.g)
            #endif
            #ifdef NORMALMAP_9
                BLEND_NORMAL(_9,  alphaBlend2.g)
            #else
                BLEND(_9,  alphaBlend2.g)
            #endif
        #endif
        #ifdef ALBEDOMAP_10
            DEFINE_COORD(_10)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_10)
                PARALLAX_BLEND(_10,  alphaBlend2.b)
            #endif
            #ifdef NORMALMAP_10
                BLEND_NORMAL(_10,  alphaBlend2.b)
            #else
                BLEND(_10,  alphaBlend2.b)
            #endif
        #endif
        #ifdef ALBEDOMAP_11
            DEFINE_COORD(_11)
            #if defined(PARALLAXMAPPING) && defined (PARALLAXMAP_11)
                PARALLAX_BLEND(_11,  alphaBlend2.a)
            #endif
            #ifdef NORMALMAP_11
                BLEND_NORMAL(_11,  alphaBlend2.a)
            #else
                BLEND(_11,  alphaBlend2.a)
            #endif
        #endif                   
    #endif
    return albedo;
}

#ifdef PROBE_COLOR
    uniform vec4 m_ProbeColor;
#endif

void main(){
    viewDir = normalize(g_CameraPosition - wPosition);
    
    vec3 norm = normalize(wNormal);
    normal = norm;
    //norm = vec3(0.5, 0.5, 1.0);
    #if defined(NORMALMAP_0) || defined(PARALLAXMAP) || defined(PLAGUEDNORMALMAP)
        vec3 tan = normalize(wTangent.xyz);
        //tbnMat = mat3(tan, wTangent.w * cross( (wNormal), (tan)), norm);
        tbnMat = mat3(tan, wTangent.w * cross( (norm), (tan)), norm);
    #endif
    
    newTexCoord=texCoord;

    //always calculated since the
    vec3 blending;
    #ifdef ALBEDOMAP_0
      #ifdef ALPHAMAP
        albedo = calculateAlbedoBlend(newTexCoord);
      #else
        albedo = texture2D(m_AlbedoMap_0, newTexCoord);
      #endif
    #endif

    if(albedo.a <= 0.1){
        albedo.r = 1.0;
        discard;
    }

    #ifdef ROUGHNESSMAP
        Roughness = texture2D(m_RoughnessMap, newTexCoord).r * Roughness;
    #endif
    Roughness = max(Roughness, 1e-4);
    #ifdef METALLICMAP   
        Metallic = texture2D(m_MetallicMap, newTexCoord).r;
    #endif

    #ifdef METALLICMAP
        Metallic = texture2D(m_MetallicMap, newTexCoord).r * max(Metallic, 0.0);
    #else
        Metallic =  max(Metallic, 0.0);
    #endif

    //---------------------
    // normal calculations
    //---------------------
    #if defined(NORMALMAP_0) || 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
     //    normal = calculateNormalTriPlanar(wNormal, wVertex, texCoord);
       #else
     //    normal = calculateNormal(texCoord);
       #endif
       normal += norm * 0.9;
       normal = normalize(normal * vec3(2.0) - vec3(1.0));
       normal.z /= 100;
       normal = normalize(normal);
    #else
       normal = normalize(norm * vec3(2.0) - vec3(1.0));
       normal = wNormal;
    #endif

 //   normal = normalize(normal * vec3(2.0) - vec3(1.0));

     #ifdef SPECGLOSSPIPELINE
        #ifdef USE_PACKED_SG
            vec4 specularColor = texture2D(m_SpecularGlossinessMap, newTexCoord);
            float glossiness = specularColor.a * m_Glossiness;
            specularColor *= m_Specular;
        #else
            #ifdef SPECULARMAP
                vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
            #else
                vec4 specularColor = vec4(1.0);
            #endif
            #ifdef GLOSSINESSMAP
                float glossiness = texture2D(m_GlossinessMap, newTexCoord).r * m_Glossiness;
            #else
                float glossiness = m_Glossiness;
            #endif
            specularColor *= m_Specular;
        #endif
        vec4 diffuseColor = albedo;// * (1.0 - max(max(specularColor.r, specularColor.g), specularColor.b));
        Roughness = 1.0 - glossiness;
        vec3 fZero = specularColor.xyz;
    #else
        float specular = 0.5;
        float nonMetalSpec = 0.08 * specular;
        vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic;
        vec4 diffuseColor = albedo - albedo * Metallic;
        vec3 fZero = vec3(specular);
    #endif

    gl_FragColor.rgb = vec3(0.0);
    vec3 ao = vec3(1.0);

    float ndotv = max( dot( normal, viewDir ),0.0);
    for( int i = 0;i < NB_LIGHTS; i+=3){
        vec4 lightColor = g_LightData[i];
        vec4 lightData1 = g_LightData[i+1];                
        vec4 lightDir;
        vec3 lightVec;            
        lightComputeDir(wPosition, lightColor.w, lightData1, lightDir, lightVec);

        float fallOff = 1.0;
        #if __VERSION__ >= 110
            // allow use of control flow
        if(lightColor.w > 1.0){
        #endif
            fallOff =  computeSpotFalloff(g_LightData[i+2], lightVec);
        #if __VERSION__ >= 110
        }
        #endif
        //point light attenuation
        fallOff *= lightDir.w;

        lightDir.xyz = normalize(lightDir.xyz);            
        vec3 directDiffuse;
        vec3 directSpecular;
        
        float hdotv = PBR_ComputeDirectLight(normal, lightDir.xyz, viewDir,
                            lightColor.rgb, fZero, Roughness, ndotv,
                            directDiffuse,  directSpecular);

        vec3 directLighting = diffuseColor.rgb *directDiffuse + directSpecular;
        gl_FragColor.rgb += directLighting * fallOff;
    }

    #if NB_PROBES >= 1
        vec3 color1 = vec3(0.0);
        vec3 color2 = vec3(0.0);
        vec3 color3 = vec3(0.0);
        float weight1 = 1.0;
        float weight2 = 0.0;
        float weight3 = 0.0;

        float ndf = renderProbe(viewDir, wPosition, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData, g_ShCoeffs, g_PrefEnvMap, color1);
        #if NB_PROBES >= 2
            float ndf2 = renderProbe(viewDir, wPosition, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData2, g_ShCoeffs2, g_PrefEnvMap2, color2);
        #endif
        #if NB_PROBES == 3
            float ndf3 = renderProbe(viewDir, wPosition, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData3, g_ShCoeffs3, g_PrefEnvMap3, color3);
        #endif

         #if NB_PROBES >= 2
            float invNdf =  max(1.0 - ndf,0.0);
            float invNdf2 =  max(1.0 - ndf2,0.0);
            float sumNdf = ndf + ndf2;
            float sumInvNdf = invNdf + invNdf2;
            #if NB_PROBES == 3
                float invNdf3 = max(1.0 - ndf3,0.0);
                sumNdf += ndf3;
                sumInvNdf += invNdf3;
                weight3 =  ((1.0 - (ndf3 / sumNdf)) / (NB_PROBES - 1)) *  (invNdf3 / sumInvNdf);
            #endif

            weight1 = ((1.0 - (ndf / sumNdf)) / (NB_PROBES - 1)) *  (invNdf / sumInvNdf);
            weight2 = ((1.0 - (ndf2 / sumNdf)) / (NB_PROBES - 1)) *  (invNdf2 / sumInvNdf);

            float weightSum = weight1 + weight2 + weight3;

            weight1 /= weightSum;
            weight2 /= weightSum;
            weight3 /= weightSum;
        #endif
        
        #if USE_AMBIENT_LIGHT
            color1.rgb *= g_AmbientLightColor.rgb;
            color2.rgb *= g_AmbientLightColor.rgb;
            color3.rgb *= g_AmbientLightColor.rgb;
        #endif
        
        gl_FragColor.rgb += color1 * clamp(weight1,0.0,1.0) + color2 * clamp(weight2,0.0,1.0) + color3 * clamp(weight3,0.0,1.0);
    #endif
 
    #if defined(EMISSIVE) || defined (EMISSIVEMAP)
        #ifdef EMISSIVEMAP
            emissive = texture2D(m_EmissiveMap, newTexCoord);
        #else
            emissive = m_Emissive;
        #endif
        gl_FragColor += emissive * pow(emissive.a, m_EmissivePower) * m_EmissiveIntensity;
    #endif

    gl_FragColor.a = 1.0;
}

vert:

#import "MatDefs/ShaderLib/GLSLCompat.glsllib"
#import "MatDefs/ShaderLib/Instancing.glsllib"

attribute vec3 inPosition;
attribute vec3 inNormal;
attribute vec2 inTexCoord;
uniform vec3 g_CameraPosition;

uniform vec4 g_AmbientLightColor; //passed to .frag for scaling light probe brightness in 3.3 compatible version of this shader.

varying vec2 texCoord;
varying vec3 wPosition;
varying vec3 wNormal;

#ifdef TRI_PLANAR_MAPPING
  varying vec4 wVertex;
#endif


#if defined(NORMALMAP_0) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(PLAGUEDNORMALMAP)
    attribute vec4 inTangent;
    varying vec4 wTangent;
#endif

varying float camDist;   

void main(){
    vec4 modelSpacePos = vec4(inPosition, 1.0);

    gl_Position = TransformWorldViewProjection(modelSpacePos);

    texCoord = inTexCoord;

    wPosition = TransformWorld(modelSpacePos).xyz;
    wNormal  = normalize(TransformWorldNormal(inNormal));
   #if  defined(NORMALMAP_0) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7)  || defined(PLAGUEDNORMALMAP)
        wTangent = vec4(TransformWorldNormal(inTangent.xyz),inTangent.w);
    #endif

    #ifdef TRI_PLANAR_MAPPING
       wVertex = vec4(inPosition,0.0);
       wNormal = inNormal;
    #endif

    camDist = distance(g_CameraPosition.xyz, wPosition.xyz);
}
3 Likes

j3md:

// NOTE: Doesn't support OpenGL1
MaterialDef PBR Terrain {

    MaterialParameters {
        Float NormalType : -1.0
        Vector4 ProbeColor

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

        Float ParallaxLODDistance : 20 

        // The emissive color of the object
        Color Emissive        
        // the emissive power
        Float EmissivePower : 3.5      
        // the emissive intensity
        Float EmissiveIntensity : 2.3

        // Specular/gloss map
        Texture2D MetallicMap -LINEAR

        // Set to Use Lightmap
        Texture2D LightMap

        Float Roughness_0 : 0.0
        Float Roughness_1 : 0.0
        Float Roughness_2 : 0.0
        Float Roughness_3 : 0.0
        Float Roughness_4 : 0.0
        Float Roughness_5 : 0.0
        Float Roughness_6 : 0.0
        Float Roughness_7 : 0.0
        Float Roughness_8 : 0.0
        Float Roughness_9 : 0.0
        Float Roughness_10 : 0.0
        Float Roughness_11 : 0.0

        Float Metallic_0 : 0.0
        Float Metallic_1 : 0.0
        Float Metallic_2 : 0.0
        Float Metallic_3 : 0.0
        Float Metallic_4 : 0.0
        Float Metallic_5 : 0.0
        Float Metallic_6 : 0.0
        Float Metallic_7 : 0.0
        Float Metallic_8 : 0.0
        Float Metallic_9 : 0.0
        Float Metallic_10 : 0.0
        Float Metallic_11 : 0.0

        // use tri-planar mapping
        Boolean useTriPlanarMapping

        // Use ward specular instead of phong
        Boolean WardIso

        // Albedo color
        Color Albedo

        // Texture map #0
        Texture2D AlbedoMap_0
        Float AlbedoMap_0_scale
        Texture2D NormalMap_0 -LINEAR
        Texture2D ParallaxMap_0 -LINEAR
        Float ParallaxHeight_0

        // Texture map #1
        Texture2D AlbedoMap_1
        Float AlbedoMap_1_scale
        Texture2D NormalMap_1 -LINEAR
        Texture2D ParallaxMap_1 -LINEAR
        Float ParallaxHeight_1

        // Texture map #2
        Texture2D AlbedoMap_2
        Float AlbedoMap_2_scale
        Texture2D NormalMap_2 -LINEAR
        Texture2D ParallaxMap_2 -LINEAR
        Float ParallaxHeight_2

        // Texture map #3
        Texture2D AlbedoMap_3
        Float AlbedoMap_3_scale
        Texture2D NormalMap_3 -LINEAR
        Texture2D ParallaxMap_3 -LINEAR
        Float ParallaxHeight_3

        // Texture map #4
        Texture2D AlbedoMap_4
        Float AlbedoMap_4_scale
        Texture2D NormalMap_4 -LINEAR
        Texture2D ParallaxMap_4 -LINEAR
        Float ParallaxHeight_4

        // Texture map #5
        Texture2D AlbedoMap_5
        Float AlbedoMap_5_scale
        Texture2D NormalMap_5 -LINEAR
        Texture2D ParallaxMap_5 -LINEAR
        Float ParallaxHeight_5

        // Texture map #6
        Texture2D AlbedoMap_6
        Float AlbedoMap_6_scale
        Texture2D NormalMap_6 -LINEAR
        Texture2D ParallaxMap_6 -LINEAR
        Float ParallaxHeight_6

        // Texture map #7
        Texture2D AlbedoMap_7
        Float AlbedoMap_7_scale
        Texture2D NormalMap_7 -LINEAR
        Texture2D ParallaxMap_7 -LINEAR
        Float ParallaxHeight_7

        // Texture map #8
        Texture2D AlbedoMap_8
        Float AlbedoMap_8_scale
        Texture2D NormalMap_8 -LINEAR
        Texture2D ParallaxMap_8 -LINEAR
        Float ParallaxHeight_8

        // Texture map #9
        Texture2D AlbedoMap_9
        Float AlbedoMap_9_scale
        Texture2D NormalMap_9 -LINEAR
        Texture2D ParallaxMap_9 -LINEAR
        Float ParallaxHeight_9

        // Texture map #10
        Texture2D AlbedoMap_10
        Float AlbedoMap_10_scale
        Texture2D NormalMap_10 -LINEAR
        Texture2D ParallaxMap_10 -LINEAR
        Float ParallaxHeight_10

        // Texture map #11
        Texture2D AlbedoMap_11
        Float AlbedoMap_11_scale
        Texture2D NormalMap_11 -LINEAR
        Texture2D ParallaxMap_11 -LINEAR
        Float ParallaxHeight_11

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

        // For Spec gloss pipeline
        Boolean UseSpecGloss
        Texture2D SpecularMap
        Texture2D GlossinessMap
        Texture2D SpecularGlossinessMap
        Color Specular : 1.0 1.0 1.0 1.0
        Float Glossiness : 1.0

        Vector4 ProbeData

        // Prefiltered Env Map for indirect specular lighting
        TextureCubeMap PrefEnvMap -LINEAR
        
        // Irradiance map for indirect diffuse lighting
        TextureCubeMap IrradianceMap -LINEAR

        //integrate BRDF map for indirect Lighting
        Texture2D IntegrateBRDF -LINEAR

        // Parallax/height map
        Texture2D ParallaxMap -LINEAR

        //Set to true is parallax map is stored in the alpha channel of the normal map
        Boolean PackedNormalParallax   

        // Set to Use Lightmap
        Texture2D LightMap

        // Set to use TexCoord2 for the lightmap sampling
        Boolean SeparateTexCoord
        // the light map is a gray scale ao map, on ly the r channel will be read.
        Boolean LightMapAsAOMap

        //shadows
        Int FilterMode
        Boolean HardwareShadows

        Texture2D ShadowMap0
        Texture2D ShadowMap1
        Texture2D ShadowMap2
        Texture2D ShadowMap3
        //pointLights
        Texture2D ShadowMap4
        Texture2D ShadowMap5
        
        Float ShadowIntensity
        Vector4 Splits
        Vector2 FadeInfo

        Matrix4 LightViewProjectionMatrix0
        Matrix4 LightViewProjectionMatrix1
        Matrix4 LightViewProjectionMatrix2
        Matrix4 LightViewProjectionMatrix3
        //pointLight
        Matrix4 LightViewProjectionMatrix4
        Matrix4 LightViewProjectionMatrix5   
        Vector3 LightPos
        Vector3 LightDir

        Float PCFEdge
        Float ShadowMapSize

        // For hardware skinning
        Int NumberOfBones
        Matrix4Array BoneMatrices
                
        //For instancing
        Boolean UseInstancing

        //For Vertex Color
        Boolean UseVertexColor

        Boolean BackfaceShadows : false
    }

    Technique {

        LightMode SinglePassAndImageBased

        VertexShader  GLSL150:   MatDefs/Terrain/plagued/terrainPBR.vert
        FragmentShader GLSL150: MatDefs/Terrain/plagued/terrainPBR.frag

        WorldParameters {
            WorldViewProjectionMatrix
            CameraPosition
            WorldMatrix
            WorldNormalMatrix
            ViewProjectionMatrix
            ViewMatrix
            Time
        }

        Defines {
            PARALLAX_LOD_DISTANCE : ParallaxLODDistance
            PARALLAXMAPPING : parallaxMapping
            EMISSIVEMAP : EmissiveMap
            SPECGLOSSPIPELINE : SpecularMap
            PROBE_COLOR : ProbeColor
            NORMAL_TYPE: NormalType
            TRI_PLANAR_MAPPING : useTriPlanarMapping
            ALBEDOMAP_0 : AlbedoMap_0
            ALBEDOMAP_1 : AlbedoMap_1
            ALBEDOMAP_2 : AlbedoMap_2
            ALBEDOMAP_3 : AlbedoMap_3
            ALBEDOMAP_4 : AlbedoMap_4
            ALBEDOMAP_5 : AlbedoMap_5
            ALBEDOMAP_6 : AlbedoMap_6
            ALBEDOMAP_7 : AlbedoMap_7
            ALBEDOMAP_8 : AlbedoMap_8
            ALBEDOMAP_9 : AlbedoMap_9
            ALBEDOMAP_10 : AlbedoMap_10
            ALBEDOMAP_11 : AlbedoMap_11
            NORMALMAP_0 : NormalMap_0
            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
            PARALLAXMAP_0 : ParallaxMap_0
            PARALLAXMAP_1 : ParallaxMap_1
            PARALLAXMAP_2 : ParallaxMap_2
            PARALLAXMAP_3 : ParallaxMap_3
            PARALLAXMAP_4 : ParallaxMap_4
            PARALLAXMAP_5 : ParallaxMap_5
            PARALLAXMAP_6 : ParallaxMap_6
            PARALLAXMAP_7 : ParallaxMap_7
            PARALLAXMAP_8 : ParallaxMap_8
            PARALLAXMAP_9 : ParallaxMap_9
            PARALLAXMAP_10 : ParallaxMap_10
            PARALLAXMAP_11 : ParallaxMap_11
        }
    }
    
}

example parallax code for material:

mat_terrain = new Material(assetManager, "MatDefs/Terrain/terrainPBR.j3md");
mat_terrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap2.png"));
Texture grass = assetManager.loadTexture("Textures/materials/Ground07/Ground07_col.jpg");
grass.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("AlbedoMap_0", grass);
mat_terrain.setFloat("AlbedoMap_0_scale", 128f);
mat_terrain.setFloat("Metallic_0", 0.2f);
mat_terrain.setFloat("Roughness_0", 0.9f);
Texture dirt = assetManager.loadTexture("Textures/materials/Ground01/Ground01_col_var1.jpg");
dirt.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("AlbedoMap_1", dirt);
mat_terrain.setFloat("AlbedoMap_1_scale", 128f);
mat_terrain.setFloat("Metallic_1", 0.3f);
mat_terrain.setFloat("Roughness_1", 0.8f);
Texture rock = assetManager.loadTexture("Textures/materials/Ground18/Ground18_col.jpg");
rock.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("AlbedoMap_2", rock);
mat_terrain.setFloat("AlbedoMap_2_scale", 128f);
mat_terrain.setFloat("Metallic_2", 1f);
mat_terrain.setFloat("Roughness_2", 0.1f);
// NORMAL MAPS
Texture tex1Normal = assetManager.loadTexture("Textures/materials/Ground07/Ground07_nrm.jpg"); //9
tex1Normal.setWrap(Texture.WrapMode.Repeat);
Texture tex2Normal = assetManager.loadTexture("Textures/materials/Ground01/Ground01_nrm.jpg");
tex2Normal.setWrap(Texture.WrapMode.Repeat);
Texture tex3Normal = assetManager.loadTexture("Textures/materials/Ground18/Ground18_nrm.jpg");
tex3Normal.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("NormalMap_0", tex1Normal);
mat_terrain.setTexture("NormalMap_1", tex2Normal);
mat_terrain.setTexture("NormalMap_2", tex3Normal);

Texture texParallax = assetManager.loadTexture("Textures/materials/Ground07/Ground07_disp.jpg");
texParallax.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("ParallaxMap_0", texParallax);

Texture texParallax2 = assetManager.loadTexture("Textures/materials/Ground01/Ground01_disp.jpg");
texParallax2.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("ParallaxMap_1", texParallax2);

Texture texParallax3 = assetManager.loadTexture("Textures/materials/Ground18/Ground18_disp.jpg");
texParallax3.setWrap(Texture.WrapMode.Repeat);
mat_terrain.setTexture("ParallaxMap_2", texParallax3);

mat_terrain.setFloat("ParallaxHeight_0", 0.06f);
mat_terrain.setFloat("ParallaxHeight_1", 0.01f);
mat_terrain.setFloat("ParallaxHeight_2", 0.02f);
mat_terrain.setBoolean("parallaxMapping", true);
mat_terrain.setFloat("ParallaxLODDistance", 30f);

I’ll be away from home without a computer or internet for the next week, but I will work on adding the code to the PBR Terrain shader as soon as I’m back.

1 Like

great :slight_smile: wish you great holidays. one week is not long though.

i already cleaned your PBR terrain from affliction and added occlusion parallax, but there are things you know better how to do like:

  • TRI PLANNAR parallax.
    because im not sure how to modify this for tri plannar:

      #define PARALLAX_BLEND(index, ab)\
      calculateParallax(coord##index, m_ParallaxHeight##index, m_ParallaxMap##index, ab);
    

there are also things to add like:

  • using NORMALMAP_PARALLAX

  • HorizonFade, because its not used in terrain pbr, but it is in new PBR material.

  • option for alternative SteepParallax - once i used occlusion parallax i dont gived possibility for steep parallax here.

  • make normals for tri plannar proper
    because im not sure if this is totally correct:

     normal += norm * 0.9;
     normal = normalize(normal * vec3(2.0) - vec3(1.0));
     normal.z /= 100;
     normal = normalize(normal);
    

and maybe something more.

once all done i think it might be added into JME.

2 Likes