Some light questions/proposals

Hello, the issue why i tought about this is that i need a list of all lights, currently i do this with following since i have not found a better solution:

[java]

for(Spatial s:Viewport.getScenes()){

for(Light light:s.getLocalLightList()){



}

}

[/java]



As you imagine, i have to recursively process the whole scenegraph. Is there actually a better approach, or a predefined list somewhere?



As next, for performance reasons i want to frustum check the lights to process only those which intersects the visible part. For this i need a bounding volume of course.



And here is the main question:

Why does light not offer a bounding volume, ok, it would not make sense for ambinet/directional light, but it would for pointlights and spotlights.

It would even make a big performance difference in the current lighting system since it would allow to get the object needed to rerender for this light. No need anymore to rerender the whole scene.



If that proposal is silly, whats the best way to do frustum checks on light?

There is planned improvements for the light system, see issue 510. No change before 3.0 though.



Right now you have to keep track and manage the lights yourself…

@zzuegg said:
And here is the main question:
Why does light not offer a bounding volume, ok, it would not make sense for ambinet/directional light, but it would for pointlights and spotlights.
It would even make a big performance difference in the current lighting system since it would allow to get the object needed to rerender for this light. No need anymore to rerender the whole scene.

If that proposal is silly, whats the best way to do frustum checks on light?

That's basically the idea behind issue 510 yeah
@nehon said:
That's basically the idea behind issue 510 yeah


I asumed.
Actually i currently do not care about the renderer itself, what i need is the bounding volume for an effective frustum check. At least i my head the changes to the light would be quickly done and would not interfere with the current system at all.

The remaining question is should i wait for the system to get implemented or should i make my own modifications and then modify my system once the system get's implemented.

Another function i would like to have is that the lights get's propagated upwards in the scenegraph and then the rootNode knows every light attached. It would cost some performance when adding lights/spatial containing lights, but it would increase performace on every renderpass.

I think a simple change to the spatial class would be enough to have that functionallity. Also this would be a fully transparent solution with no changes to the existing api

You can propose a patch if you want but be warned that issue 510 is currently being worked on by Kirill and there is no guaranty your patch will be applied.

Just a fast question that arrived when looking trought the current implementation.



What’s the advantage of using the LightList construct instead of a ArrayList?

Well you’ll have to ask Kirill, but from what I see LightList does a lot more than a simple ArrayList.

Jeah i know that it does more, i was simply curious why this design was used in stead of a ‘classic’ ArrayList+Helpers.

tbh i don’t know, this was done a long time before i’ve entered the core team.

I try to cull light in single pass material, when the light uniforms are passed. They are passed as arrays so the location/bounding calculations are only done once. Not sure how effective it is, and i haven’t had time to try the actual code yet, but the concept is simple.



This is from the single pass material file. The stuff inside “if (cullLights)” is what i just added, but havent run yet. Everything else is working as usual.



[java]

@Override

protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {

if (numLights == 0) { // this shader does not do lighting, ignore.

return;

}



LightList worldLightList = g.getWorldLightList();

ColorRGBA ambLightColor = new ColorRGBA(0f, 0f, 0f, 1f);



if (cullLights) {

Vector3f center = g.getWorldTranslation().add(g.getModelBound().getCenter());

float span = calculateSpan(g); // just gets sqrt(3) times the maximum extent (x y or z). i.e. the radius of the smallest sphere that contains all the geometry.



for (int i = 0; i < worldLightList.size(); i++) { // For each light in the list.

Light light = worldLightList.get(i);

if (light instanceof AmbientLight) {

ambLightColor.addLocal(light.getColor());

} else if (light instanceof DirectionalLight) {

lightList.add(light);

} else if (light instanceof PointLight) { // Decide if the point light is within range.

PointLight pl = (PointLight) light;

Vector3f lightPos = pl.getPosition();

float radius = pl.getRadius(); // This would be the “span” of the light - not using quadratic falloff here.

float dist = lightPos.distance(center);

if (radius + span >= dist) {

lightList.add(light);

}

} else if (light instanceof SpotLight) { // Same idea, just do a check for spotlight cone as well.



// just something i use to avoid spotlight calculations in shaders if spotlights arent even used.

if ( (Boolean) this.getParam(“EnableSpotlights”).getValue() == false) {

continue;

}



// And same thing as with point lights, basically.

SpotLight sl = (SpotLight) light;

Vector3f lightPos = sl.getPosition();

float range = sl.getSpotRange();

Vector3f dir = center.subtract(lightPos);

float dist = dir.length();

// If the geometry is within distance of the spot light.

if (range + span >= dist) {

// If it’s within “side distance” of the spot light.

Vector3f sDir = sl.getDirection();

float cosAngle = sDir.dot(dir.normalize());

if(cosAngle <= 0){

continue;

}

float cosOuter = sl.getPackedAngleCos() - (int)sl.getPackedAngleCos();

if (cosAngle > cosOuter) {

lightList.add(light);

}

}

} // if spotlight

} // for each light

} else { // if light list is not to be culled (standard)

for (int i = 0; i < worldLightList.size(); i++) {

Light light = worldLightList.get(i);

if (light instanceof AmbientLight) {

ambLightColor.addLocal(light.getColor());

} else {

lightList.add(light);

}

}

}



// Just the normal processing. It’s the same with or without “culling”.

numLights = lightList.size();

this.setInt(“NumLights”, numLights);



Uniform lightColor = shader.getUniform(“g_LightColor”);

Uniform lightPos = shader.getUniform(“g_LightPosition”);

Uniform lightDir = shader.getUniform(“g_LightDirection”);



lightColor.setVector4Length(numLights);

lightPos.setVector4Length(numLights);

lightDir.setVector4Length(numLights);



Uniform ambientColor = shader.getUniform(“g_AmbientLightColor”);

ambLightColor.a = 1.0f;

ambientColor.setValue(VarType.Vector4, ambLightColor);



for (int i = 0; i < numLights; i++) {

Light l = lightList.get(i);

ColorRGBA color = l.getColor();

lightColor.setVector4InArray(color.getRed(),

color.getGreen(),

color.getBlue(),

l.getType().getId(),

i);



switch (l.getType()) {

case Directional:

DirectionalLight dl = (DirectionalLight) l;

Vector3f dir = dl.getDirection();

lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, i);

break;

case Point:

PointLight pl = (PointLight) l;

Vector3f pos = pl.getPosition();

float invRadius = pl.getInvRadius();

lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, i);

break;

case Spot:

SpotLight sl = (SpotLight) l;

Vector3f pos2 = sl.getPosition();

Vector3f dir2 = sl.getDirection();

float invRange = sl.getInvSpotRange();

float spotAngleCos = sl.getPackedAngleCos();



lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, i);

lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, i);

break;

default:

throw new UnsupportedOperationException("Unknown type of light: " + l.getType());

}

}



lightList.clear(); // The temporary list.

}

[/java]





EDIT: Hmm maye the scale of the geometry should be taken into account. Not sure if that’s calculated into the bounding box. Either way its the same principle.



EDIT: The short explanation would be - for each light, instead of just adding it to the light uniforms, check first if its within proper distance to the geometry (if its actually affecting it). The only modification to the single pass routine is that trivial check for point- and spotlights. They have to be processed anyways…

I am currently tracking down an issue with my lighting,



I could be wrong, but following code looks wrong: (Material.java line 693)

[java]

for (int i = 0; i < numLights; i++) {

if (lightList.size() <= i) {

lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);

lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);

} else {

Light l = lightList.get(i);

ColorRGBA color = l.getColor();

lightColor.setVector4InArray(color.getRed(),

color.getGreen(),

color.getBlue(),

l.getType().getId(),

i);



switch (l.getType()) {

case Directional:

DirectionalLight dl = (DirectionalLight) l;

Vector3f dir = dl.getDirection();

lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightIndex);

break;

case Point:

PointLight pl = (PointLight) l;

Vector3f pos = pl.getPosition();

float invRadius = pl.getInvRadius();

lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);

break;

case Spot:

SpotLight sl = (SpotLight) l;

Vector3f pos2 = sl.getPosition();

Vector3f dir2 = sl.getDirection();

float invRange = sl.getInvSpotRange();

float spotAngleCos = sl.getPackedAngleCos();



lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);

lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);

break;

case Ambient:

// skip this light. Does not increase lightIndex

continue;

default:

throw new UnsupportedOperationException("Unknown type of light: " + l.getType());

}

}



lightIndex++;

}

[/java]



Afaik it should be:



[java]

for (int i = 0; i < numLights; i++) {

if (lightList.size() <= i) {

lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);

lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);

} else {

Light l = lightList.get(i);

ColorRGBA color = l.getColor();

lightColor.setVector4InArray(color.getRed(),

color.getGreen(),

color.getBlue(),

l.getType().getId(),

i);



switch (l.getType()) {

case Directional:

DirectionalLight dl = (DirectionalLight) l;

Vector3f dir = dl.getDirection();

lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightIndex);



lightDir.setVector4InArray(0, 0, 0, 0, lightIndex);



break;

case Point:

PointLight pl = (PointLight) l;

Vector3f pos = pl.getPosition();

float invRadius = pl.getInvRadius();

lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);



lightDir.setVector4InArray(0, 0, 0, 0, lightIndex);



break;

case Spot:

SpotLight sl = (SpotLight) l;

Vector3f pos2 = sl.getPosition();

Vector3f dir2 = sl.getDirection();

float invRange = sl.getInvSpotRange();

float spotAngleCos = sl.getPackedAngleCos();



lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);

lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);

break;

case Ambient:

// skip this light. Does not increase lightIndex

continue;

default:

throw new UnsupportedOperationException("Unknown type of light: " + l.getType());

}

}



lightIndex++;

}

[/java]



It does not solve my isse but it looks more logical to me