Backstory: Going back all the way to 2014 when The Last of Us remastered released on the PS4, something particular caught my attention. The light from the flashlight the player carried had real-time GI bounce - basically shining the light into a dark room bounced around (or at least mimicked that) and lit up the room! Here's an example from a screenshot in-game:
It's subtle (as it should be) but it's there
So I wrote my version of this in Unreal Engine 4 with some C++ and custom HLSL node. I did this a few years ago and looking back at it I'm sure there's a lot of room for improvement!
My result:
Breakdown:
Spawned a Scene capture 2D at the player's position and attached to camera
In C++, I did a ray cast every other frame to a certain distance in front of the camera
I used the Scene capture 2D to sample only the base color (with a defined view frustum and sample resolution) at the ray cast hit point
ran the sampled base color and stored it to a Render Texture
Ran the render texture through a shader and calculated the average color from the texture stored in render texture (every frame)
separated out the output of the above step to R,G,B buckets and created a Light function material from it
I spawned 3 movable non shadow casting, diffuse only point lights each assigned with the light function material from the corresponding R,G,B buckets
Make sure the spawned point lights' positions are updated every other frame a few defined units in front of the ray hit location
Well there you go! That's it
Some small parameterization that I did
Strength of all the point lights are scaled up and down based on the flashlight strength
currently the Render texture is sampled at 16 pixels (4x4) but this could be changed for more accuracy
Multiple bounce maybe?
The Light function:
Simple HLSL code to average colors from a texture:
float redBucket = 0.0f;
float greenBucket = 0.0f;
float blueBucket=0.0f;
float i=0.0f;
float j =0.0f;for(i = -1.0f;i<=1;i+=1/Res)
{
for(j = -1.0f;j<=1;j+=1/Res)
{
float4 sample = Texture2DSample(Tex,TexSampler,float2(i,j));
redBucket+=sample.x;
greenBucket+=sample.y;
blueBucket+=sample.z;
}
}return float3(redBucket/Res,greenBucket/Res,blueBucket/Res);
Notes:
Obviously this method isn't perfect and has a lot of room for improvement:
I ended up turning up specular contribution from the point lights to save on performance. So basically I don't get any indirect specular from the flashlight
The tool still takes to >2ms to render which can use some optimization
Would like to imitate specular bounce when flashlight is pointed at something like a mirror