Lighting Performance

It might be possible to bake in some lighting, but I would like dynamic lighting for projectiles and explosions, much like Descent 3. I also don’t want to increase the vertex count of my surfaces to facilitate vertex lighting. Seeing single-pass lighting shaders (and possibly even DeferredRendering shading), it sounds like I’ll be able to do what I want. I’m going to try writing my own shader, doing pretty much what survivor is doing, but a bit more stripped down and customized for my situation.

Muwhahahah!



http://i.imgur.com/dA0Q5.png

No way. Did you optimize the rendering pipeline? Why aren’t you showing us some stats? :stuck_out_tongue:

http://i.imgur.com/MODls.png


This is using FXAA & Bloom filters, and every projectile has a dynamic light attached to it doing per-fragment lighting... this is on my laptop's graphics card, which is an NVidia NVS 4200M. 100-120 FPS on this scene when shooting all those shots. Actually not sure how good this is, and how well it will scale down to cheapo cards like the Intel HD...

How’s it working? Multiple lights per pass?

All lights are handled in a single pass. It is pretty much working just like survivors stuff here: http://hub.jmonkeyengine.org/groups/contribution-depot-jme3/forum/topic/single-pass-lighting/



I did write my own shader, not sure how much faster, if any, it is from survivor’s. I’m not doing any specular stuff. I’ve been playing with about 20 different lights, and recycling them as needed. I have an “AddLight(Position, Color, Brightness, Importance)” function that will find one of the 20 light “slots” that is less important than the one we want to add, and then it will replace it. Projectiles are of low importance, but explosions and level lights are high importance. When lights are destroyed, they are set to zero importance.



The shader then iterates over the 20 light slots, quickly skipping lights that are off or too far away…



I’m surprised on how poor the built-in lighting system is… you basically have to write your own lighting shaders to get any decent performance. I think survivor’s lighting should just replace the current default system, which is just way too slow.

http://code.google.com/p/jmonkeyengine/issues/detail?id=510

btw “just using survivors lighting” would render some features in jme unusable

Good to see it is officially reported as a defect. Also good to know that there ARE workarounds that are not too difficult to implement, as discussed here. :slight_smile:

@phr00t said:
Good to see it is officially reported as a defect.

Not the lighting system itself, the lack of ways to optimize. As said, switching to deferred lighting has implications and btw there was a skeletal deferred shader all the time in the engine.

I’ve read some of the implications of Deferred Lighting, but this single-pass system does not share those implications. However, it does have a “fixed” number of lights it can support at a time (which the programmer could change depending on how many simultaneous lights they need). However, that fixed number is always going to be far greater than the 2 or 3 the current lighting system can support without coming to a screeching halt. jMonkeyEngine is some good stuff, but not having a decent lighting system out-of-the-box is likely a big turn off for new developers. I really suggest you guys look into replacing the current lighting system with something like survivor’s as soon as possible.


btw “just using survivors lighting” would render some features in jme unusable


However, you have to admit, the current entire lighting system is practically unusable at this point, which is a bigger problem that needs solving.
@phr00t said:
However, you have to admit, the current entire lighting system is practically unusable at this point, which is a bigger problem that needs solving.

Again, it cannot be optimized enough and it could be made easier to switch to other lighting systems depending on the needs. The movable point lights are one aspect of lighting. And I fear John Carmack also likes this way of lighting better so you'll have to argue with him or with @Momoko_Fan about the "usability" of this system.

If it cannot be optimized enough to be useful, shouldn’t it just be swapped out for something better? We can always add John Carmack’s method or Momoko_Fan’s method later so programmers will have those options, but it should be a priority to provide some usable lighting system out-of-the-box.

@phr00t said:
We

lol?

@phr00t: Looking forward to a fix to issue 510 :wink:

Also note that just because it runs on your latest and great card with hardware flow control and OpenGL4.2, doesn’t mean its going to run on that old ATI card from 2005 with no hardware flow control and baseline OpenGL2.0.



The jME3 material system supports multiple techniques so you could create one that will be used automatically in OpenGL3 with optimized handling. If not supported, it will fallback to using the OpenGL2 technique.

@normen said:
lol?


Sorry for implying other people may offer help to improve jMonkeyEngine's lighting system, including myself? This type of attitude turns me off, and possibly others. Progress would be much easier without it.

I apologize if this is somehow offensive to you, but it does need to be pointed out how much of a problem the lighting system is for jMonkeyEngine. This engine is so advanced in many areas, so it is very disheartening to see the lighting engine, which is such a key element to 3D games, in its current state. It is nice to see other people working on viable solutions, which should be incorporated into the main build. Again, I don't intend this to be somehow offensive.

I’d like to test it out on older hardware. Is there something I am doing that may not be compatible with OpenGL3 or OpenGL2?



This is my .vert:



[java]#define NUM_LIGHTS 12



uniform mat4 g_WorldViewProjectionMatrix;

uniform mat4 g_WorldMatrix;

uniform mat3 g_WorldMatrixInverseTranspose;



uniform vec4 g_LightPosition[NUM_LIGHTS];

uniform vec4 g_LightColor[NUM_LIGHTS];



attribute vec3 inPosition;

attribute vec2 inTexCoord;

attribute vec3 inNormal;

varying vec2 texCoord1;

varying vec3 vNormal;

varying vec3 inpos;



void main(){

vec4 position = vec4(inPosition, 1.0);

texCoord1 = inTexCoord;

vNormal = g_WorldMatrixInverseTranspose * inNormal;

gl_Position = g_WorldViewProjectionMatrix * position;

inpos = vec3(g_WorldMatrix * position);

}

[/java]



This is my .frag (I also do some built-in directional lighting & ambient lighting):



[java]#define NUM_LIGHTS 12



uniform vec4 m_Color;

uniform vec4 m_AddColor;

uniform vec4 m_TopColor;

uniform vec4 m_LeftColor;

uniform vec4 m_RightColor;



uniform vec4 g_LightPosition[NUM_LIGHTS];

uniform vec4 g_LightColor[NUM_LIGHTS];



uniform sampler2D m_ColorMap;



varying vec2 texCoord1;

varying vec3 vNormal;

varying vec3 inpos;



void main(){

vec4 texcolor;

vec4 topcolor;

vec4 leftcolor;

vec4 rightcolor;



texcolor = texture2D(m_ColorMap, texCoord1);



#ifdef HAS_COLOR

texcolor *= m_Color;

#endif



// assign the directional lights

float topamount = dot(vec3(0.0, 1.0, 0.0), vNormal);

if( topamount > 0.0 ) topcolor = texcolor * m_TopColor * topamount;

float leftamount = dot(vec3(-1.0, -1.0, -1.0), vNormal);

if( leftamount > 0.0 ) leftcolor = texcolor * m_LeftColor * leftamount;

float rightamount = dot(vec3(1.0, -1.0, 1.0), vNormal);

if( rightamount > 0.0 ) rightcolor = texcolor * m_RightColor * rightamount;



gl_FragColor = 0.35 * (topcolor + leftcolor + rightcolor) + texcolor * 0.1;



#ifdef HAS_ADDCOLOR

gl_FragColor += m_AddColor;

#endif



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

vec4 lightColor = g_LightColor;

if( lightColor.a > 0.0 ) {

vec3 lightVector = g_LightPosition.xyz - inpos;

float brightness = lightColor.a - length(lightVector) * 0.075;

if( brightness > 0.0 ) {

float normal = dot(lightVector, vNormal);

if( normal < 0.3 ) normal = 0.3;

brightness *= normal;

if( brightness > 1.25 ) brightness = 1.25;

gl_FragColor.r += lightColor.r * brightness * texcolor.r;

gl_FragColor.g += lightColor.g * brightness * texcolor.g;

gl_FragColor.b += lightColor.b * brightness * texcolor.b;

}

}

}



// apply original alpha

gl_FragColor.a = texcolor.a;

}[/java]



and the j3md:



[java]MaterialDef FogUnshaded {



MaterialParameters {

Texture2D ColorMap



Color Color ( Color )



Color AddColor

Color TopColor

Color LeftColor

Color RightColor



Color GlowColor

}



Technique {

VertexShader GLSL100: Shaders/SimpleLighting.vert

FragmentShader GLSL100: Shaders/SimpleLighting.frag



WorldParameters {

WorldMatrix

WorldViewProjectionMatrix

WorldMatrixInverseTranspose

}



Defines {

SEPARATE_TEXCOORD : SeparateTexCoord

HAS_COLORMAP : ColorMap

HAS_COLOR : Color

HAS_ADDCOLOR : AddColor

}

}



Technique PreNormalPass {



VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert

FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag



WorldParameters {

WorldMatrix

WorldViewProjectionMatrix

WorldMatrixInverseTranspose

}



RenderState {



}



}



Technique Glow {



VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert

FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag



WorldParameters {

WorldViewProjectionMatrix

}



Defines {

NEED_TEXCOORD1

HAS_GLOWMAP : GlowMap

HAS_GLOWCOLOR : GlowColor

}

}



Technique FixedFunc {

}



}

[/java]



EDIT: Ha, it is still called “FogUnshaded” because I pulled some parts from my 3079 game :stuck_out_tongue:

Big improvement found!



http://i.imgur.com/82xsU.png



I changed the .frag shader loop to look like this:



[java] vec4 lightColor;

vec3 lightVector;

float brightness, normal, len;

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

lightColor = g_LightColor;

if( lightColor.a > 0.0 ) {

lightVector = g_LightPosition.xyz - inpos;

len = lightVector.xlightVector.x + lightVector.ylightVector.y + lightVector.zlightVector.z;

brightness = lightColor.a
lightColor.a - len * 0.004;

if( brightness > 0.0 ) {

normal = dot(lightVector, vNormal);

if( normal < 0.3 ) normal = 0.3;

brightness *= normal;

if( brightness > 1.25 ) brightness = 1.25;

gl_FragColor.rgb += lightColor.rgb * brightness * texcolor.rgb;

}

}

}[/java]



… which gets rid of the length(…) call, which was significantly slowing things down. I took the same type of screenshot above, and there is a +70 FPS difference with this shader change. If I disable FXAA and the Bloom filter, I was getting frame rates in the 700-1000 FPS range. This is where it’s at! :smiley:



EDIT: You may not see the projectile lights working in the above screenshot, but they are… they are just too far away from a wall, unfortunately. When my game’s FPS runs too high, my projectiles don’t fly fast enough and die out before reaching close enough (bug to fix!). I tested the projectiles by shooting them close to a wall, and in other rooms, and the performance is very improved with the same visual results.

1 Like

Looks like a very promising approach Phroot. It even has a constant number of times around the loop so I don’t see why it wouldn’t be viable even on older graphics cards. It might be an idea to upload a simple example somewhere as an applet and ask loads of people to give it a try.



What is there in here that might not work on all cards? The passing in of arrays of lights?



I’m only a noob on shader stuff but isn’t all that branching bad for performance too?

@phr00t said:
Sorry for implying other people may offer help to improve jMonkeyEngine's lighting system, including myself? This type of attitude turns me off, and possibly others. Progress would be much easier without it.

Yeah, right. So as momko said, looking forward to a solition.
1 Like
@phr00t said:
if( normal < 0.3 ) normal = 0.3;

You'd better use max(0.3,normal); that does the same and avoid a branching.

We know that the lighting could use some enhancements, I already said that we'll implement deferred rendering for 3.1, that is IMO THE solution to implement for multiple lights.
Saying that the lighting system is unusable, is kinda unfair....The thing is that you look at it from your very own point of view.
We have to make a lighting system that works on 2005 machines and 2012....and of course that this lighting system looks good in any situation. Ho and I forgot, that it should be super fast with plenty lights.

There are many examples on this forum of games working with the current lighting system, and i'm sorry if I'm mistaken, but it seems you are yourself selling a game based on JME with this system, aren't you?

Survivor's lighting system looks nice, but it comes with other limitations, so it's all a question of pros and cons. It seems that it suits your needs right now, and I hope that won't change when customers with low Hardware configuration will come to you.

There are many alternatives to full dynamic lighting , some tricks that has been used for long in the video game industry, baking static shadows and lights into textures, partitioning the scene and and just keep dynamic light/shadows for what's is really dynamic).
1 Like