Best way to scale Light Probe for day/night?

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