Best way to scale Light Probe for day/night?

I’m trying to figure out how to send a value to my shaders to represent the sun light, that way I can scale the light probe to be darker at night.

I got the day/night cycles with the light probe to work by updating a uniform on all my objects every few frames based on the time of day, but this is really slow.

Unfortunately it looks like the Light Probe does not use its color value

Does anybody know if there’s a way to get a light probe’s color value in the shader? It seems like the best solution to achieving day/night cycles with a light probe - as of now it seems to be not possible with PBR, unless you regenerate the probe or use extra probes.

Unless anyone has any other ideas for day/night cycles with a light probe, any input is appreciated. :slightly_smiling_face:

Otherwise my last resort option is to instead send a 1 pixel Texture to every material, and use that single pixel to store a float that represents the time of day… this way I could just update that single pixel based on the time of day and send the texture to each object just once on init, rather than updating 100s of uniform values which is too slow.

But it seems like it would be much easier to figure out how to access a Light Probe’s color in the shader and use that value instead

Why not add a color material parameter to your material? Then all of your materials could just share that instance and you only have to update the one color object.

I did this with the PBR shaders for SpaceBugs because I wanted to control the ambient brightness. It bugged me that it 100% came from the light probe (I use a generic light probe)… so I essentially use a color as an “ambient filter” which lets me control the brightness and color of the ambient light. (Direct light is already covered by the regular light color.)

I’m just going to paste the whole material so you can compare it to regular PBR… I didn’t add much but it was a while ago and I don’t know if PBR has changed since.

This is the FilteredPBR.j3md:

MaterialDef Filtered PBR Lighting {

    MaterialParameters {

        // Alpha threshold for fragment discarding
        Float AlphaDiscardThreshold (AlphaTestFallOff)

        //metalness of the material
        Float Metallic : 1.0
        //Roughness of the material
        Float Roughness : 1.0        
        // Base material color
        Color BaseColor : 1.0 1.0 1.0 1.0
        // The emissive color of the object
        Color Emissive        
        // the emissive power
        Float EmissivePower : 3.0        
        // the emissive intensity
        Float EmissiveIntensity : 2.0

        // BaseColor map
        Texture2D BaseColorMap

        // Metallic map
        Texture2D MetallicMap -LINEAR
        
        // Roughness Map
        Texture2D RoughnessMap -LINEAR

        //Metallic and Roughness are packed respectively in the b and g channel of a single map
        // r: unspecified
        // g: Roughness
        // b: Metallic
        Texture2D MetallicRoughnessMap -LINEAR
        
        // Texture of the emissive parts of the material
        Texture2D EmissiveMap

        // Normal map
        Texture2D NormalMap -LINEAR

        //The type of normal map: -1.0 (DirectX), 1.0 (OpenGl)
        Float NormalType : -1.0

        // 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

        // Parallax/height map
        Texture2D ParallaxMap -LINEAR

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

        //Sets the relief height for parallax mapping
        Float ParallaxHeight : 0.05       

        //Set to true to activate Steep Parallax mapping
        Boolean SteepParallax

        //Horizon fade
        Boolean HorizonFade

        // 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 Morph animation
        FloatArray MorphWeights
        Int NumberOfMorphTargets
        Int NumberOfTargetsBuffers
                
        //For instancing
        Boolean UseInstancing

        //For Vertex Color
        Boolean UseVertexColor

        Boolean BackfaceShadows : false
        
        Vector4 FilterColor
    }

    Technique {
        LightMode SinglePassAndImageBased
        
        VertexShader GLSL110 GLSL150:   Common/MatDefs/Light/PBRLighting.vert
        FragmentShader GLSL110 GLSL150: MatDefs/FilteredPBR.frag

        WorldParameters {
            WorldViewProjectionMatrix
            CameraPosition
            WorldMatrix
            WorldNormalMatrix
            ViewProjectionMatrix
            ViewMatrix
        }

        Defines {         
            BASECOLORMAP : BaseColorMap            
            NORMALMAP : NormalMap
            METALLICMAP : MetallicMap
            ROUGHNESSMAP : RoughnessMap
            EMISSIVEMAP : EmissiveMap
            EMISSIVE : Emissive
            SPECGLOSSPIPELINE : UseSpecGloss
            PARALLAXMAP : ParallaxMap
            NORMALMAP_PARALLAX : PackedNormalParallax
            STEEP_PARALLAX : SteepParallax
            LIGHTMAP : LightMap
            SEPARATE_TEXCOORD : SeparateTexCoord
            DISCARD_ALPHA : AlphaDiscardThreshold                        
            NUM_BONES : NumberOfBones                        
            INSTANCING : UseInstancing
            USE_PACKED_MR: MetallicRoughnessMap
            USE_PACKED_SG: SpecularGlossinessMap
            SPECULARMAP : SpecularMap
            GLOSSINESSMAP : GlossinessMap
            NORMAL_TYPE: NormalType
            VERTEX_COLOR : UseVertexColor
            AO_MAP: LightMapAsAOMap
            NUM_MORPH_TARGETS: NumberOfMorphTargets
            NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
            HORIZON_FADE: HorizonFade
            FILTER_COLOR : FilterColor
        }
    }


    Technique PreShadow {

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

        WorldParameters {
            WorldViewProjectionMatrix
            WorldViewMatrix
            ViewProjectionMatrix
            ViewMatrix
        }

        Defines {
            DISCARD_ALPHA : AlphaDiscardThreshold
            NUM_BONES : NumberOfBones
            INSTANCING : UseInstancing
            NUM_MORPH_TARGETS: NumberOfMorphTargets
            NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
        }

        ForcedRenderState {
            FaceCull Off
            DepthTest On
            DepthWrite On
            PolyOffset 5 3
            ColorWrite Off
        }

    }


    Technique PostShadow{
        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag

        WorldParameters {
            WorldViewProjectionMatrix
            WorldMatrix
            ViewProjectionMatrix
            ViewMatrix
        }

        Defines {
            HARDWARE_SHADOWS : HardwareShadows
            FILTER_MODE : FilterMode
            PCFEDGE : PCFEdge
            DISCARD_ALPHA : AlphaDiscardThreshold           
            SHADOWMAP_SIZE : ShadowMapSize
            SHADOWMAP_SIZE : ShadowMapSize
            FADE : FadeInfo
            PSSM : Splits
            POINTLIGHT : LightViewProjectionMatrix5
            NUM_BONES : NumberOfBones
            INSTANCING : UseInstancing
            BACKFACE_SHADOWS: BackfaceShadows
            NUM_MORPH_TARGETS: NumberOfMorphTargets
            NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
        }

        ForcedRenderState {
            Blend Modulate
            DepthWrite Off                 
            PolyOffset -0.1 0
        }
    }

    Technique PostShadow{
        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag

        WorldParameters {
            WorldViewProjectionMatrix
            WorldMatrix
            ViewProjectionMatrix
            ViewMatrix
        }

        Defines {
            HARDWARE_SHADOWS : HardwareShadows
            FILTER_MODE : FilterMode
            PCFEDGE : PCFEdge
            DISCARD_ALPHA : AlphaDiscardThreshold           
            SHADOWMAP_SIZE : ShadowMapSize
            FADE : FadeInfo
            PSSM : Splits
            POINTLIGHT : LightViewProjectionMatrix5
            NUM_BONES : NumberOfBones
            INSTANCING : UseInstancing
            BACKFACE_SHADOWS: BackfaceShadows
            NUM_MORPH_TARGETS: NumberOfMorphTargets
            NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
        }

        ForcedRenderState {
            Blend Modulate
            DepthWrite Off   
            PolyOffset -0.1 0  
        }
    }

    Technique PreNormalPass {

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

        WorldParameters {
            WorldViewProjectionMatrix
            WorldViewMatrix
            NormalMatrix
            ViewProjectionMatrix
            ViewMatrix
        }

        Defines {
            NUM_BONES : NumberOfBones
            INSTANCING : UseInstancing
            NUM_MORPH_TARGETS: NumberOfMorphTargets
            NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
        }

    }

    Technique Glow {

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

        WorldParameters {
            WorldViewProjectionMatrix
            ViewProjectionMatrix
            ViewMatrix
        }

        Defines {
            NEED_TEXCOORD1
            NUM_BONES : NumberOfBones
            INSTANCING : UseInstancing
            NUM_MORPH_TARGETS: NumberOfMorphTargets
            NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
        }
    }

}

FilteredPBR.frag:

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

varying vec2 texCoord;
#ifdef SEPARATE_TEXCOORD
  varying vec2 texCoord2;
#endif

varying vec4 Color;

uniform vec4 g_LightData[NB_LIGHTS];
uniform vec3 g_CameraPosition;

uniform float m_Roughness;
uniform float m_Metallic;

varying vec3 wPosition;    


#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 BASECOLORMAP
  uniform sampler2D m_BaseColorMap;
#endif

#ifdef USE_PACKED_MR
     uniform sampler2D m_MetallicRoughnessMap;
#else
    #ifdef METALLICMAP
      uniform sampler2D m_MetallicMap;
    #endif
    #ifdef ROUGHNESSMAP
      uniform sampler2D m_RoughnessMap;
    #endif
#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 vec4 m_Specular;
  uniform float m_Glossiness;
  #ifdef USE_PACKED_SG
    uniform sampler2D m_SpecularGlossinessMap;
  #else
    uniform sampler2D m_SpecularMap;
    uniform sampler2D m_GlossinessMap;
  #endif
#endif

#ifdef PARALLAXMAP
  uniform sampler2D m_ParallaxMap;  
#endif
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
    uniform float m_ParallaxHeight;
#endif

#ifdef LIGHTMAP
  uniform sampler2D m_LightMap;
#endif
  
#if defined(NORMALMAP) || defined(PARALLAXMAP)
  uniform sampler2D m_NormalMap;   
  varying vec4 wTangent;
#endif
varying vec3 wNormal;

#ifdef DISCARD_ALPHA
uniform float m_AlphaDiscardThreshold;
#endif

#ifdef FILTER_COLOR
uniform vec4 m_FilterColor;
#endif

void main(){
    vec2 newTexCoord;
    vec3 viewDir = normalize(g_CameraPosition - wPosition);

    vec3 norm = normalize(wNormal);
    #if defined(NORMALMAP) || defined(PARALLAXMAP)
        vec3 tan = normalize(wTangent.xyz);
        mat3 tbnMat = mat3(tan, wTangent.w * cross( (norm), (tan)), norm);
    #endif

    #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
       vec3 vViewDir =  viewDir * tbnMat;  
       #ifdef STEEP_PARALLAX
           #ifdef NORMALMAP_PARALLAX
               //parallax map is stored in the alpha channel of the normal map         
               newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
           #else
               //parallax map is a texture
               newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);         
           #endif
       #else
           #ifdef NORMALMAP_PARALLAX
               //parallax map is stored in the alpha channel of the normal map         
               newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
           #else
               //parallax map is a texture
               newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
           #endif
       #endif
    #else
       newTexCoord = texCoord;    
    #endif
    
    #ifdef BASECOLORMAP
        vec4 albedo = texture2D(m_BaseColorMap, newTexCoord) * Color;
    #else
        vec4 albedo = Color;
    #endif

    #ifdef USE_PACKED_MR
        vec2 rm = texture2D(m_MetallicRoughnessMap, newTexCoord).gb;
        float Roughness = rm.x * max(m_Roughness, 1e-4);
        float Metallic = rm.y * max(m_Metallic, 0.0);
    #else
        #ifdef ROUGHNESSMAP
            float Roughness = texture2D(m_RoughnessMap, newTexCoord).r * max(m_Roughness, 1e-4);
        #else
            float Roughness =  max(m_Roughness, 1e-4);
        #endif
        #ifdef METALLICMAP
            float Metallic = texture2D(m_MetallicMap, newTexCoord).r * max(m_Metallic, 0.0);
        #else
            float Metallic =  max(m_Metallic, 0.0);
        #endif
    #endif
 
    float alpha = albedo.a;

    #ifdef DISCARD_ALPHA
        if(alpha < m_AlphaDiscardThreshold){
            discard;
        }
    #endif
 
    // ***********************
    // Read from textures
    // ***********************
    #if defined(NORMALMAP)
      vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
      //Note the -2.0 and -1.0. We invert the green channel of the normal map, 
      //as it's complient with normal maps generated with blender.
      //see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898
      //for more explanation.
      vec3 normal = normalize((normalHeight.xyz * vec3(2.0, NORMAL_TYPE * 2.0, 2.0) - vec3(1.0, NORMAL_TYPE * 1.0, 1.0)));
      normal = normalize(tbnMat * normal);
      //normal = normalize(normal * inverse(tbnMat));
    #else
      vec3 normal = norm;
    #endif

    #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);

    #ifdef LIGHTMAP
       vec3 lightMapColor;
       #ifdef SEPARATE_TEXCOORD
          lightMapColor = texture2D(m_LightMap, texCoord2).rgb;
       #else
          lightMapColor = texture2D(m_LightMap, texCoord).rgb;
       #endif
       #ifdef AO_MAP
         lightMapColor.gb = lightMapColor.rr;
         ao = lightMapColor;
       #else
         gl_FragColor.rgb += diffuseColor.rgb * lightMapColor;
       #endif
       specularColor.rgb *= lightMapColor;
    #endif


    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);
                            
        //#ifdef FILTER_COLOR
        //    directSpecular.rgb *= m_FilterColor.rgb;
        //#endif
                            
        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
 
        #ifdef FILTER_COLOR
            color1.rgb *= m_FilterColor.rgb;
            color2.rgb *= m_FilterColor.rgb;
            color3.rgb *= m_FilterColor.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
            vec4 emissive = texture2D(m_EmissiveMap, newTexCoord);
        #else
            vec4 emissive = m_Emissive;
        #endif
        gl_FragColor += emissive * pow(emissive.a, m_EmissivePower) * m_EmissiveIntensity;
    #endif
    gl_FragColor.a = alpha;
   
}

The important parts are related to the added FilterColor.

With this I was able to have red overhead lighting and set a low red ambient color or pretty much tweak the ambient lighting any way I wanted.

And for the future, if you have ever to have a ‘constantly modifying’ float on a bunch of shaders, just use a Vec3 or Color to hold the values and then you can share instances. (Or setting a single material parameter override on the scene root works also.)

3 Likes

Yeah I’m in the same situation now it seems like. I’m trying to setup day/night cycles as well as indoor lighting for caves and dark areas that should not be 100% lit by the probe like you mentioned. I will switch my shader to send a Color the way you do in your shader, and then mine should finally have accurate lighting. I also changed my fork of the shader to use the vertex colors as the ambient brightness of the probe color, which is nice for altering the indoor lighting near doors and windows.

I never knew that, but now I do :slightly_smiling_face: that should be useful to know going forward.

Thanks alot for your help, as always :slightly_smiling_face:

I also changed my fork of the shader to use the vertex colors as the ambient brightness of the probe color, which is nice for altering the indoor lighting near doors and windows.

what do you mean, how do this look like?

I used the Vertex paint tool in blender to paint certain parts of a model brighter where there should be more exposure to the sun (so inside a cave is almost entirely black), and then I just pass the vertex colors to the fragment shader and multiply that by the FilterColor from Paul’s example above (although i refer to it as sun brightness

#if defined(USE_VERTEX_COLORS_AS_SUN_INTENSITY)
    varying vec4 vertColors;
#endif

uniform vec4 m_SunBrightness;

...

float sunIntesnity = m_SunBrightness;

#if defined(USE_VERTEX_COLORS_AS_SUN_INTENSITY)
       sunIntesnity *= vertColors.r;
#endif

then use sunIntesity to scale the light probe color down the same way FilterColor is used in the Paul’s code.

I also found that in addition to scaling the LightProbe, you could also use that value to scale down the directional light and ambient light in the shader. This is useful for models that need complete sun at some parts, but complete darkness in others (like a cave entrance that gradually gets darker as you go deeper, but still needs full lighting from point lights, and cannot be removed from the node containing the Directional Light)

hmm, thats a tricky :smiley:

but in professional studios i belive they do this different way. (im not shader guru so i even dont know how)

what i planned to try was making bloom much more intense while in interiors(or dark areas) that could make a nice effect. (like the eye need time to adapt :)) not much related but idk if you thought about it too

yeah it feels kind of convoluted, especially since I’ll also have to update the shader for the players and enemies to darken based on the nearby environment to make it appear smooth.

I still have a lot to do before it looks accurate and I can get a good gif, but here’s my progress so far. You can really notice the main player is still receiving sunlight in the cave
https://i.imgur.com/5Afurap.gif

and this is the cave model that has its vertex colors painted so the entrance receives sunlight and fades to darkness smoothly
https://i.imgur.com/5ht4bDz.png

That sounds like it would be a nice touch, and would add a better sense of realism to the lights in dark areas. I don’t know much about post processing and screen filters, but I wonder if that would be the best way to do so, by creating a screen filter that intensifies the contrast when you look at bright lights in dark places

2 Likes

hmm, but wait.

if you will just make your character PBR material based, then you can make it receive surrounding lighting intensity(based on envmap intensity).

because as i see your character use Lighting.j3md

if you dont want make 9999 light probes for your terrain(that i managed to observe is required), you could just do temporarily to see effect doing so:
make one light probe, update its location to player location each some seconds(once it finish build envmap).

you can even speed it up making envmap really low resolution.

Then your character will receive light(or rather texture color) based on probe envmap.
you just need make character metalness little higher to get it.

ofc this temporary solution is wrong trully, because you should just put multiple probes per some distance in game map.

I recall @nehon mentioning that its best to not generate lots of light probes at run time, and I had thought its best to pre-generate them, although I can’t remember for certain and I can’t find the post I’m remembering. But my scene just uses about 7-8 probes total to cover the map and seems to run alright without performance issues.

The character is using PBRLighting although the inaccurate lighting in the cave makes it look otherwise - I just haven’t updated the character’s shader to match the cave’s shader. The lightprobe in the cave is actually realy bright, it just appears otherwise because i use the vertex colors to scale it down in the shader.

1 Like

The lightprobe in the cave is actually realy bright, it just appears otherwise because i use the vertex colors to scale it down in the shader.

it should not be bright IMO. if cave is dark, then lightprobe should receive this dark rendered texture and pass it to character so character using metalness would receive this darkness.

About light probes in my situation i cant pregenerate them, because scene is builded from random elements. What i thought i will do, was try generate them in-run(but only near ones), like i do now with one that follow player.(temporary solution)

btw. how you do holes in JME Terrain to enter your cave model? Because as i see you use JME terrain system right?

Yeah although the way I do it could be considered a somewhat irregular process. I use the standard JME terrain, and I painted over some spots with a fully transparent texture. Then I edited my scene in the SDK so that there is a BoundingSphere over top of the part of the terrain that is transparent. And then when I do collisions with rays, I omit any collisions with the terrain that happen inside that Bounding Sphere. That way the player can walk through the transparent terrain in just that one area.

But its important to mention that I don’t use BulletPhysics for character movement and collisions, I just use rays and bounding spheres - so I don’t know if it would be as easy to achieve passable holes in the terrain using a BetterCharacterControl

i belive shadows will have problem.

anyway everything else looks working fine for you. again tricky :smiley:

myself i would try just remove part of terrain mesh, but not sure if it would not break it.(LOD/etc)

btw. dont worry about tricky ways. It remind me how fallout 3 made train in their game.

as i remember. Train was helmet on character below terrain and this character was moving on path so helmet(train) too :smiley: really not sure why they dont had normal object path follow :rofl:

1 Like

yeah thats another issue I’ll have to solve with the shadows - but I already had this issue with other transparent objects that are receiving shadows where the mesh is fully transparent, so I’ll have to spend some time working on that regardless, but I’ll have to wait for another day when I have time to delve into the shadow renderer and learn how that works.

That was my first thought, but then I realized my terrain patches are too big :frowning: I set my terrains at a size of 512 with a patch size of 216, so there’s only 16 patches per terrain to minimize object count. I’d like to use a smaller patch size so I could just delete the patch for a hole, but any smallerthan 216 and the FPS slows down drastically from rendering too many objects.

Lol that’s an interesting way to do it. I’d like to see what that looks from the developers perspective :joy: thats a smart idea though to reuse their AI code, and save the hassle of creating a new object type for the train.

1 Like

@pspeed can we add your solution into standard JME PBRLigthing ?

Edit: I can make a PR if everybody agree.

3 Likes

agree, default no color will just make no change.

when not defined, it will also not affect shader speed.(because it will use IF_DEF)

just if anyone need, he will be able to use it

2 Likes

Yeah, it would be nice. There are a bunch of situations where it is useful.

2 Likes

Okay, will make a PR soon.
btw, any reason you are using Vector4f instead of Color for FilterColor in MaterialDef ?

Probably because in the heat of the moment, I can never remember if color allows direct manipulation and I know that Vector4f does.

There’s probably no good reason.

Edit: note that whatever the case, it’s important to be able to set one and modify it later without having to set the material parameter all the time. I’m pretty sure that works for both ColorRGBA and Vector4f… but I already automatically know it works for Vector4f.

1 Like

In my adaptation, I used the rgb channel as a Filter color exactly how Paul does in his example, and then I used the alpha channel as a single float for the sunlight’s brightness value for scaling the probes brightness

I think my experience was that the sunlight brightness was controlled by the regular lighting… so lowering the DirectionalLight would also lower the intensity of the specular, etc…