My progress for a baked Light Probe system for moving objects in Range engine :D

As you know, Bge, Upbge, Range has that problem that there’s no real indirect lighting … so unless you full your scenes with tons of lamp ( with all the light bleeding it can causes and the severe harm it does on your fps) … your scene will be pretty dull and ugly (get only a single sun as light source to see how ugly your game will look like) . Ofc you can change the shadow color for not having it completely black and so it will be less harsh , but the problem remains : it looks so flat.

And so, Upbge 0.25/Range 1.0 is shipped with that combo of 2 hemi + Environement lighting to give some colors : a global blueish color with some hard orange to emulate some light bouncing off the ground … it roughly do the job : it looks less ugly and your scene is supposed to be happening outdoors.

But , when i came some times ago to a problem : how the hell can i get ride of that static global environement lighting that chase my character even when she is like in a 100 meter deep cave … Why , my character can have only 2 shades : exposed to the sun (with/without EL) and not exposed to the sun (with/without EL) …

The answer is in that often ignored/discarded input of the Extended Material node . That input provides
image
That input provides how much your material will confirm or cancel the global Environment lighting . Interesting thing : its point-based and not an uniform value → so providing a color texture will give details

And so , for static objects, you just need to provide an AO map and you are done .

Btw, for some reasons, there’s that myth that AO map has to be multiplied with the Diffuse map … it’s completely wrong to do so : color x black = black … you cannot retrieve your colors by adding source lights … your colors are gone , you cant go back. On the opposite : that Ambient input indicates how much you want to get ride of the Environment Lighting only .

Now, for the dynamic objects : problem, the typical baking cannot work ofc … for some reasons, i never encounter some concerns about this problem in the upbge community except some attempt done with a 2d filter that bugs with moving cameras … but there’s a solution i was thinking about and discovered later that such solution exist in games engines like Unity :

Light Probe !

image

The idea is simple : its to provide a global lighting but with local variations

Before get known on those things, i was thinking about solutions and some people here came with the idea of a ray to either read the closest vertex on the ground or the UV of the poly on the ground … as you might guess problem is when you get off that ground or that your character has to climb some stairs …


Due to the ray technic, the persian gonna get the ambiant color of the deepness before even falling

So, finally i come with this Light-Probe-3D-baking idea i would like to see implemented in Range engine

Get AO values in 3d space

--> bake on Vertex Colors of  a Grid mesh the AO
--> Record the Values
--> Move the Grid +1 unit on the Z - Repeat the 2 above
--> Create a 3d Matrix of all the Values
--> Record it in a file

In game

first tic :
→ import that 3d matrix of 3d AO values [0,1]
when character is moving :
→ compute the worldposition of your character regarding the virtual 8 surrounding points of a cube in which your character is positionned into at that moment
→ compute a barrymetric weight to assign for each of those 8 surrounding points according their distance to the worldPosition of your character
→ compute a global score as being [weights] x [AO]
→ assign that weighted AO value to your object.color

import bge
from math import floor
from mathutils import Vector
## get the AO 3d matrix
from table import mm

scene = bge.logic.getCurrentScene()
cont = bge.logic.getCurrentController()
obj = scene.objects['Suzanne']

### X,Y,Z
point = obj.worldPosition 
x , y , z = point
f = floor(x) ;  g = floor(y) ;  h = floor(z)

## virtual cube in which your character is standing in
cube = [(f,g,h),(f+1,g,h),(f,g+1,h),(f+1,g+1,h),
(f,g,h+1),(f+1,g,h+1),(f,g+1,h+1),(f+1,g+1,h+1)]

## retrieve associated AO values  by Z,Y,X
values = [ mm[i[2]][i[0]][i[1]] for i in cube]

## just compute once the max normalized length existing in a cube 
maxl = 3**0.5

## measure the 8 distances
dists = [(Vector(i) - Vector(point)).length for i in cube]
## normalized score
barry = [1-i/maxl for i in dists]
## sum of weights must be 1.0
sumb = sum(barry)
ws = [i/sumb for i in barry]
## final weighted AO score
ao = sum([i*j for i, j in zip(ws,values)])

## either you bake on high-vertex Grid , either you lerp a bit
obj.color = obj.color.lerp((ao,ao,ao,1.0),0.5)

Plug it to the Ambiant input (red, green, blue, they are all the same - only the level 0-1 matters)

image

Result (sun shadows are off and i baked also the AO on the castle so one can compare)

https://i.imgur.com/NQDhDrZ.mp4

soon : instead of using the Object color, i will use the Light Data node for making the Ambiant not uni-dimensional

2 Likes

interior faces play effects on the light probe (even if the character is not meant to go through walls) . That can play an undesirable effect if you get too close to a probe that is located inside a wall

gonna have to cancel the AO contribution from interior faces by marking them by a way or another so that they have no weight in the light probe system. Maybe a 2 passes baking on the grid : 1* bake some strong red environment lighting so that only exterior faces will be red , 2* record values in a red matrix, 3* bake AO , 4* compute an AO matrix regarding the red matrix values

1 Like

can someone tell me if what i’m doing here is useful or obsolete ? Reflective cubemapping will acts like cpu consuming mirror surface, i’m wrong ? Did i miss something ?

Reminder : this is about setting dynamically the level of Environment lighting acceptance through the ‘Ambient’ material channel

Anything similar has been done already ?

Uhhmm :thinking: Looks incredible works well so far!
Does it work in UPBGE well?

Fred/K.S

All that matters is if it works for you, if it satisfies you.

There are similar methods, but use 3D textures to store the occlusion, this allows more complex calculations at shader level.
You should check this page about Interactive Rendering Algorithms, most of the methods focus on ambient occlusion and global illumination.

lol, thx. It’s nothing else than storing AO baked values in a 3d matrix. Nothing sophisticated . Yes, works with UPBGE too ofc.

the goal is more to launch an idea in hope it can be improved and maybe even integrated in next Range : a light probe system

i forgot to post how i loop bake and record into AO matrix. So this is a script to execute in Blender.
The target object is a Grid mesh with vertex spaced 1 Blender Unit.
The bake target is Vertex Colors and it’s an AO bake.

import bpy
from math import floor
from mathutils import Vector

obj = bpy.context.scene.objects['Grid']

n = 33   ## size of the square matrix
i = 0    ## start at level 0 ( = ground)
l = 17   ## number of levels / iteration ( = the Z height of the matrix) 

## function that gonna read the vertex colors of the Grid  and record them in a 2d Matrix
def print_vertex(obj,n,i):

    print("------------------------" + str(i) + "--------------------------")

    mesh = obj.data 
    vertices = obj.data.vertices
    vcol_layer = mesh.vertex_colors.active
    
    m = [[None for _ in range(n)] for _ in range(n)]  ## create an empty 2D matrix
 
    for poly in mesh.polygons :
        for loop_index in poly.loop_indices:
            loop_vert_index = mesh.loops[loop_index].vertex_index

            ## get the world coordinate and the color of the vertex 
            coord = tuple(obj.matrix_world * vertices[loop_vert_index].co )
            rgba = vcol_layer.data[loop_index].color[0:3]  

            ## record highest value (since AO is BW, they are all the same anyway)
            m[int(coord[0])][int(coord[1])] =   max(rgba)  
            
    return(m)            
    
           
zz = []     ##   initiate 3d matrix   
 
for i in range(0,l) : 
    obj.location.z = i   ## set the grid Z position
    bpy.ops.object.bake_image()  ## bake AO to Vertex 
    mm = print_vertex(obj,n,i)  ## get the output of the function (2d Matrix)
    zz.append(mm)  ## add it to all the previous 2d Matrix
    
print(zz)
print(["z is : " , len(zz) , "y is : " , len(zz[0]) , "x is : " , len(zz[0][0]) ] )

## save 3d AO matrix to file
myfile=open('C:\\Users\\ChuckNorris\\Desktop\\bakedAOMatrix.txt','w')
myfile.write(str(zz))
myfile.close()

Great.
Lot better than Upbge default very basic lighting and shadows that has no global illumination at all.

UPBGE 0.3+ supports Screen Spaced Global Illumination.

technically it’s more about setting the amount of “Environment lighting” validated

Material Ambient = Environment lighting * Light Probe ( white = 1 … greys… black = 0)

So for instance with this light probe, there’s nothing enlightening the object.

It’s a new feature or isn’t it just some embedded SSAO 2d filter ? If you have an window wide open, hidden on your screen and with no direct sun ray going though it : if your room is still dark, this is not GI.
I even wonder if SSGI makes sense… :thinking:

Baked lightmap ?
Is there some showcase and tutorial ?