Baked Lights with Realtime Ambient?

Hi, first off I’m making a Mobile game, so my realtime lighting options are limited.

I’m trying to achieve an effect where I can go between night/day lighting. I only wish to change the intensity/color of light, the direction of the sun won’t change, just the effect of the world getting darker.

Problem is, in order to make it feel dark I need to bake the ambient at a dark value so that when I adjust the light intensity to something like 0.1, the realtime light matches with the baked light. This presents a problem when it’s day time, the ambient is so dark on the baked objects making the scene look very unnatural. Changing the ambient color has no effect on baked objects.

The solution is to use realtime GI with realtime ambient. But that is burning up my poor tiny little iPhone 5s.

I’m pretty new to lighting, usually someone else does this stuff, but unfortunately it’s up to me now. Wondering if there are any solutions to this problem for Mobile?

I’ve tired making a white cubemap and setting that to my “Environment Relfections” source. While that does boost the blacks, everything kinda looks washed out.

Should I look at the post processing stack for this? Seeing as the sun won’t move, maybe I can just color grade the scene?

You could bake all Lights and multiply the Lightmap with a color on runtime.
I can’t tell you how to do that exactly but with this approche there is absolutly no light that needs to be calculated in realtime.

yeah I think something like that is my only option here. Modifying all shaders used on static objects so their lightmaps are multiplied by a color. Seems odd to me this doesn’t exist already and that ambient light cannot be changed for static objects unless realtime GI is used. Probably because it contributes to indirect light, so without recalculating that the change in ambient wouldn’t be correct. But… I’ve already got indirect light baked and the difference (I think) of just tinting something black vs recalculating ambient is minimal.

Guess I’ll head over to shader land and override the surface shader GI function

Another way would be postprocessing. You can render your Camera in HDR and use the grading tools to animate brightness and Tint.
But I have no idea if this is fast enought for mobile.

It’s not, it would lead to a fairly large perf degradation and not even work on older mobiles (HDR) as it depends on support.

But stuff like using lightprobes and blending between lightmaps works a treat still. Also you don’t really need HDR for grading I thought?

You will need HDR if you want to change day to night or you will get extrem bandings.

  1. Blending 2 baked lightmaps is the way to go I think, especially if you are performance conscious. I did it with a lerp within my own shader, but there could potentially be easier ways.

  2. Blending between two sets of light probes was a bit problematic though (or at least, I couldn’t figure it out at the time).

  3. Faking it with post processing color correction is also perfectly doable. You don’t need HDR. You just might be a bit more prone to banding if you push things too far.

If he starts with day and darkens everything (and maybe a blue tint), he shouldn’t get too much banding. Usually banding appears when you’re stretching the dynamic range, not when you’re compressing it further.

Ah ok, so I’ll still need to override the GI calculation to perform the lightmap blending correct?

I’ve come across a lightmap switcher, but I guess that’s just hard cuts between different lighting scenarios

Anyone know what I’m doing wrong here?

I pretty much copied the syntax of the “custom GI” example here: Unity - Manual: Surface Shader lighting examples

but I just get:

Shader error in 'Mobile/Diffuse Static': Fragment program 'frag_surf': Unrecognized sampler 'samplerunity_lightmap' - does not match any texture and is not a recognized inline name (should contain filter and wrap modes).
(on d3d11)

Compiling Fragment program with DIRECTIONAL SHADOWS_SCREEN LIGHTMAP_ON LIGHTMAP_SHADOW_MIXING SHADOWS_SHADOWMASK
Platform defines: UNITY_NO_DXT5nm UNITY_ENABLE_REFLECTION_BUFFERS UNITY_NO_CUBEMAP_ARRAY UNITY_NO_SCREENSPACE_SHADOWS UNITY_PBS_USE_BRDF2 SHADER_API_DESKTOP UNITY_HARDWARE_TIER3 UNITY_COLORSPACE_GAMMA

If I comment out “gi = UnityGlobalIllumination(data, 1.0, s.Normal);” then no error, but no GI calculation.
If I comment out “gi.indirect.diffuse = half3(0, 1, 0);” then no error, but no custom GI.

So…

The example uses the inbuilt functions. Is this some limitation of SurfaceOutput not being the same as SurfaceOutputStandard

:frowning:

Shader "Mobile/Diffuse Static" {
  
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
  
    SubShader {
      
        Tags { "RenderType"="Opaque" }
        LOD 150

        CGPROGRAM
        #pragma surface surf StaticGI noforwardadd

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        inline fixed4 LightingStaticGI(SurfaceOutput s, UnityGI gi) {
          
            return LightingLambert(s, gi);
        }

        inline void LightingStaticGI_GI(SurfaceOutput s, UnityGIInput data, inout UnityGI gi) {

            gi = UnityGlobalIllumination(data, 1.0, s.Normal);

            //test
            gi.indirect.diffuse = half3(0, 1, 0);
        }

        void surf(Input IN, inout SurfaceOutput o) {
          
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
      
        ENDCG
    }

    Fallback "Mobile/VertexLit"
}

why is this happening??

gi.indirect.diffuse *= half3(0, 1, 0); //Works
gi.indirect.diffuse = half3(0, 1, 0); //Error