My next pathetic attempt with shadows - help

Ok, I know that my 3D knowledge is terrible, I also find that it’s very hard to me to find myself in 3D math… My attempt to switch form business software development into 3D gamedev is not so easy that I wanted to.

BUT, finally I have some idea and I hope it will work.
The idea is to use modified PointLightShadowRenderer. The renderer in its standard form renders shadows for one light and of course does not know anything about other lights. In the result we can have a shadow (cast by light A) just next to light source B. In my renderer’s version I’m passing to shader all other nearby lights (positions xyz + InvRadius) and the number of them as well.
I can compile the code, there are no errors, it works somehow when I just modify the ‘shadow’ value, setting it to 0 or 1 (scene is dark or light).

I have all necessary modified files, my own versions of Lighting.j3md, PostShadow.j3md, PostShadow15.frag.

Now the code:

Updating shader and material values inside MyPointLightShadowRenderer.java:

[java]
Technique technique = material.getActiveTechnique();
if (technique == null)
return;

    LightList list = _lightNode.getLocalLightList();
    ArrayList<PointLight> arr = new ArrayList<PointLight>(list.size());
    
    Light l;
    float dist = 0;
    
    for (int i = 0; i < list.size(); i++)
    {
    	l = list.get(i);
    	if (l == light) continue;
    	if (!(l instanceof PointLight)) continue;
    	
    	PointLight p = (PointLight)l;
    	
    	dist = p.getPosition().distance(light.getPosition());
    	if (dist > p.getRadius() * 2) continue;
    	
    	arr.add(p);
    }
    
    if (arr.size() == 0) return;
    
    Uniform lightPos = null;
    Vector3f pos = null;
    PointLight pl = null;
    
    try
    {
    	material.getMaterialDef().addMaterialParam(VarType.Int, "LightCount", arr.size(), null);
    	material.setInt("LightCount", arr.size());
    	//material.getMaterialDef().addMaterialParam(VarType.Vector3Array, "LightsPositions", null, null);
    	

        Shader shader = technique.getShader();
    	
        lightPos = shader.getUniform("g_LightsPositions");
        if (lightPos.getValue() == null) return;
    	
    	for (int i = 0; i < arr.size(); i++) 
    	{
    		pl = arr.get(i);

			pos = pl.getPosition();
			float radius = pl.getInvRadius();
			lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), radius, i);
			
    	}	
    	
    	//Uniform lightPos = shader.getUniform("g_LightPosition");
    	
    }
    catch (Exception e) 
    { 
    	e.printStackTrace();
    }

[/java]

The above code updates both MyLighting and MyPostShadow materials. Again, all values are successfully passed.

Now the shader, MyPostShadow15.frag, only main methos is modified:

[java]
#ifdef NUM_LIGHTS
uniform vec4 g_LightsPositions[NUM_LIGHTS];
uniform mat4 g_WorldMatrix;
#endif

void main(){

#ifdef DISCARD_ALPHA
    #ifdef COLOR_MAP
         float alpha = texture2D(m_ColorMap,texCoord).a;
    #else    
         float alpha = texture2D(m_DiffuseMap,texCoord).a;
    #endif
  
    if(alpha < m_AlphaDiscardThreshold){
        discard;
    }
#endif

float shadow = 1.0;
#ifdef POINTLIGHT         
        shadow = getPointLightShadows(worldPos, m_LightPos,
                       m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
                       projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
#else
   #ifdef PSSM
        shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
                       m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
                       projCoord0, projCoord1, projCoord2, projCoord3);
   #else 
        //spotlight
        shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
   #endif
#endif   

#ifdef FADE
  shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
#endif
  
 
// MY CODE BEGINS HERE -----------------------------------------------  
#ifdef NUM_LIGHTS
if (shadow < 1.0)
{
	vec3 lightVector;
	vec4 lightPosition;
	float attenuation;
	
	float shadsub = 0.0;

	for (int i = 0; i < NUM_LIGHTS; i++)
	{
		lightPosition = g_LightsPositions[i];
		//calculateLightVector(lightPosition, lightVector, attenuation);
		
		vec4 vLightPos = g_WorldMatrix * vec4(lightPosition.xyz,1.0);
		vec4 vPos = g_WorldMatrix * worldPos;        
		lightVector = vLightPos.xyz - vPos.xyz;
		//lightVector = lightPosition.xyz - v_wsPosition;
		float dist = length(lightVector);

		
		lightVector /= vec3(dist);
		attenuation = clamp(1.0 - lightPosition.w * dist, 0.0, 1.0); //
		
		shadsub += attenuation;
		
	}
	
	shadsub = clamp(shadsub, 0.0, 1.0);
	shadow += shadsub;
	//shadow = 0.0; // CZAAAARNE
}

#endif
    // MY CODE ENDS HERE -----------------------------------------------------
  
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); 

//shadow = 0.0;
outFragColor =  vec4(shadow, shadow, shadow, 1.0);

}
[/java]

I’m just unable to make a formula that modify in correct way the ‘shadow’ value. The lights from g_LightsPositions should lighten the calculated shadow, make him even disappear if the light is close enough.

Any clues?

2 Likes

updated code:

[java]
lightPosition = g_LightsPositions[i];

		vec4 vLightPos = vec4(lightPosition.xyz,1.0); // world position for sure      
		vec4 vPos = worldPos; // world position too.             
		lightVector = vLightPos.xyz - vPos.xyz; // 

		float dist = length(lightVector) / 2;

		if (dist < 0.5) shadsub = 0.8;
		else if (dist < 0.8) shadsub = 0.7;
		else if (dist < 1.2) shadsub = 0.6;
		else if (dist < 1.7) shadsub = 0.5;
		else if (dist < 2.3) shadsub = 0.4;
		else if (dist < 2.8) shadsub = 0.3;
		else if (dist < 3.2) shadsub = 0.2;
		else if (dist < 3.7) shadsub = 0.1;

[/java]

It works somehow… but ofc I have a big problem with understanding which position, matrix, world variable I need to use…

http://s27.postimg.org/b4qwq8v1v/3ddu11.png

Finally…

http://s29.postimg.org/c5iu2ry07/3ddu12.png

But there are few other problems with shadows, I’ll describe everything tomorrow, if anyone wants to help.

Here is a code I’m using, maybe it can help to some other lame guy like me :wink:

MyPostShadow15.vert:
[java]
varying vec4 myPos;


void main(){
vec4 modelSpacePos = vec4(inPosition, 1.0);

#ifdef NUM_BONES
Skinning_Compute(modelSpacePos);
#endif
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
myPos = gl_Position; // ADD

// thne rest does not matter and was not changed
[/java]

.frag:
[java]
void main(){

#ifdef DISCARD_ALPHA
    #ifdef COLOR_MAP
         float alpha = texture2D(m_ColorMap,texCoord).a;
    #else    
         float alpha = texture2D(m_DiffuseMap,texCoord).a;
    #endif
  
    if(alpha < m_AlphaDiscardThreshold){
        discard;
    }
#endif

float shadow = 1.0;
#ifdef POINTLIGHT         
        shadow = getPointLightShadows(worldPos, m_LightPos,
                       m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
                       projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
#else
   #ifdef PSSM
        shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
                       m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
                       projCoord0, projCoord1, projCoord2, projCoord3);
   #else 
        //spotlight
        shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
   #endif
#endif   

#ifdef FADE
  shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
#endif
  
 
// MY CODE ----------------------------  
#ifdef NUM_LIGHTS
if (shadow < 1.0)
{
	vec3 lightVector;
	vec4 lightPosition;
	float attenuation;
	
	float shadsub = 0.0;

	for (int i = 0; i < NUM_LIGHTS; i++)
	{
		lightPosition = g_LightsPositions[i];
		
		vec4 vLightPos = vec4(lightPosition.xyz,1.0); // world position for sure       
		vec4 vPos = myPos; //worldPos; // world position too.               
		lightVector = vLightPos.xyz - vPos.xyz; // 
		float dist = length(lightVector);

		if (dist < 1.0) shadsub = 0.99;
		else if (dist < 1.1) shadsub = 0.9;
		else if (dist < 1.3) shadsub = 0.8;
		else if (dist < 1.5) shadsub = 0.7;
		else if (dist < 1.7) shadsub = 0.6;
		else if (dist < 1.9) shadsub = 0.5;
		else if (dist < 2.0) shadsub = 0.4;
		else if (dist < 2.2) shadsub = 0.3;

	}
	
	shadsub = clamp(shadsub, 0.0, 1.0);
	shadow += shadsub;
	//shadow = 0.0; // CZAAAARNE
}

#endif
    // /MY CODE -------------------------------
  
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); 

outFragColor =  vec4(shadow, shadow, shadow, 1.0);

}

[/java]

Well from what I see it looks far from pathetic. Very few people get head down into shader code.

Only “bad” thing I see at first glance is the “If else” sequence. Branching in shader in general is slow, so you have to avoid it as far as possible. You can’t really get rid of the for loop, but the ifs can maybe be replace by a step function
https://www.opengl.org/sdk/docs/man/html/step.xhtml
Or even some kind of attenuation function based on “dist”.

Thank you for advice. I’ll continue my work on shadows.

EDIT:
To fast calculation I’ll use a simple, linear function: shadsub = -0.5 * dist + 1.45;

And for more than one additional light source I need only one, max value of shadsub.
(untested code - need to be compiled, I’ll do it later at home)
[java]
// MY CODE —————————-
#ifdef NUM_LIGHTS

float maxss = 0;
   
if (shadow < 1.0)
{
	vec3 lightVector;
	vec4 lightPosition;
	float attenuation;

	float shadsub = 0.0;

	for (int i = 0; i < NUM_LIGHTS; i++)
	{
		lightPosition = g_LightsPositions[i];

		vec4 vLightPos = vec4(lightPosition.xyz,1.0); // world position for sure
		vec4 vPos = myPos; //worldPos; // world position too.
		lightVector = vLightPos.xyz – vPos.xyz; //
		float dist = length(lightVector);

		shadsub = -0.5 * dist + 1.45;
		maxss = max(maxss, shadsub);
		if (maxss >= 1.0) break; // Dont need more loops.
	}

	maxss = clamp(maxss, 0.0, 1.0);
	shadow += maxss;

}

#endif
    // /MY CODE ——————————-

[/java]

The next thing is to soften the shadow in situation, where it is far away from its own light source. Hmm, now sounds like a piece of cake :wink:

Looks great so far, actually shadows are probably one of the most complex (rendering) topics you decided to work on. (The other one would be deferred rendering)
:slight_smile:

1 Like

Shadows are very important in my current project. In dungeon crawler games the lights and darkness creates most of the game’s mood.

2 Likes

It is not as good as it looked before …

Seems like the whole effect is just a coincidence.
During tests i found that g_LightsPositions contains only zeroes…
Just debugged the shader using this:

[java]
lightPosition = g_LightsPositions[i];

		if (lightPosition.x > 8.0)
		{
			outFragColor =  vec4(0.0, 0.0, 0.0, 1.0);
		}
		else if (lightPosition.x == 0.0)
		{
		    outFragColor =  vec4(1.0, 1.0, 0.0, 1.0);
		}
		else
		{
		    outFragColor =  vec4(1.0, 0.0, 0.0, 1.0);
		}
		return;

[/java]

So I have a problem in the java/material level, but I’m sure that I’m writing the correct values into uniform. Debugged it minute ago.

Lets see, the codes:

MultiPointLightShadowRenderer is the modified PointLightShadowRenderer, it have an extra function updatePostShadowMatParams.

The class extends MyAbstractShadowRenderer, whih is, again, a copy of AbstractShadowRenderer with some necessary modifications. the main things are:

  • declaration of abstract void updatePostShadowMatParams(Material material);
  • call to that function in the begining of postFrame, using postshadowMat as a parameter.
  • in init we have new Material(assetManager, “MatDefs/Shadow/PostShadowMS.j3md”);

updatePostShadowMatParams is called again in MultiPointLightShadowRenderer inside setMaterialParameters.

So, updatePostShadowMatParams is called for every material. PostShadowMS.j3md used for renderer and LightingMS.j3md used for objects (walls, roof etc). Position is written into uniform, tested it step-by-step.

Here are materials:
LightingMS.j3md - http://pastebin.com/yyJ3fBk6
PostShadowMS.j3md - http://pastebin.com/eFbBjEXp
Those are almost exact copies of Lighting and PostShadow, few variables were added.

So… Where I messed something?
Really, need help with that.

Test scene: http://postimg.org/image/yl0ks0tbd/

I was forced to pass lights as material parameters, now I’m able to pass up to 6 lights. Seems like globals works only before renderers starts their work.

Next question (I hope this time someone will answer).

http://postimg.org/image/bsab1dccd/

Look at the image, notice the shadows in red circles - some shadow is missing. I see it everywhere, on every edge.
It’s on both - my shadow renderer and standard JME3’s PointLightShadowRenderer.

What can cause such effect? How to avoid it? Where to look at, to correct it?
I also noticed that enabling debug for renderrer (should show shadowmaps) shows me only white boxes… again, what can be the cause?

I will be happy if someone responds…

This is happening because the shadows are multiplied on top of the first render pass ( basically the shadows in the shaded areas are applied twice).
If you want to avoid it you’ll need to do your entire lighting calculation in the shadow pass, and use the darker value between shadow and light calculation for each fragment.

If you aren’t rendering all lights with shadows, you can render the unshadowed lights during the first pass and the ones with shadows in your shadow renderer pass with render state set to additive blending (thats how I am doing it).

basically you’ll do something like this in your shadow renderers fragment shader:
[java]
vec4 color = vec4(0.0);
for (i < numberOfShadowedLights; i++){
color += min(shadow(light[i]), directlighting(light[i]));
}
[/java]

1 Like

Actually this is happening because of the polygon offset on the shadow render pass
In the j3md in the pre shadow techniques :

  ForcedRenderState {
            FaceCull Off
            DepthTest On
            DepthWrite On
            PolyOffset 5 3 <= play with that
            ColorWrite Off
        }

in the post shadows

        ForcedRenderState {
            Blend Modulate
            DepthWrite Off                 
            PolyOffset -0.1 0 <= this one should have less impact but still
        }

Note that this is here to avoid Z fighting between the rendered shadows over the rest of the scene, So As Perjin mentionned the real only way to make this work properly is to render shadows in the lighting pass.
Though, you can try to reduce the poly offset of the pre pass and then use a shadow filter instead of a shadow renderer (though if you modified the shaders it may be quite some work).

@nehon
Setting the PolyOffset to 0.9 3 reduced the effect, now it is almost invisible. Thank you.

@Perjin
Given my current level of knowledge, I think that it is too hard for me. But thank you, I’m sure that I’ll return to the shadow’s code more than once during next months. Hope that I’ll understand more than now :wink: I’m playing with shaders only for a week or so.

Latest screen: http://postimg.org/image/iupihh5av/

Maybe my current code is not perfect, but at least it meets all the requirements and (according to few opinions) it generates the ‘mood’ :wink:

Looks very good, nice work

@FrozenShade said: Latest screen: http://postimg.org/image/iupihh5av/

Maybe my current code is not perfect, but at least it meets all the requirements and (according to few opinions) it generates the ‘mood’ :wink:

It looks really great. If you are accepting input, I have a few suggestions though you may have already thought of them. I’ll hold my peace just in case. :slight_smile:

Edit: note: not input about the shadow technique itself but the scene and ambient lighting… baked in stuff.

Sure, please write here any suggestions.
But… there are no ambient light right now, only another point light that follows the player and is not casting the shadow. Eventually all the lights will flicker softly.

@FrozenShade said: Sure, please write here any suggestions. But.. there are no ambient light right now, only another point light that follows the player and is not casting the shadow. Eventually all the lights will flicker softly.

Random scene suggestions… this is what I would do if it were my scene (and it’s cool enough that I almost wanted to make my own) :slight_smile:

-use a different texture for the floor even if it’s just a dirtier version of the walls.
-bake some ambient occlusion into the geometry… or at least the walls. Even just darkening the top and bottom of the wall texture a bit would help. If you go with dirtier floors then you could even dirty the bottom edge a bit.
-nitpicky stuff… but if you could find a way for your corners to align properly it would be better:

And I sort of feel like these comments may be too soon. You are clearly going to do a lot more with this since it looked like even the grate was made of stone and I suspect you will change that and many other things.

…but those are the things I first thought of when I got over my initial “wow”.

Those are only sketches with some default texture everywhere, so don’t worry, we will replace them.
We will keep walls flat but pillars will be more complex than now.

For now I’m implementing level editor which generates entire level in 3D world, we choose the size of the individual components and see how it all looks. Final models will come later.

Baked ambient occlusion is a nice idea, but in this case (dungeons are made of blocks, they are not static) it’s a bit hard to do. Of course we will try to make some tricks.

@FrozenShade said: Those are only sketches with some default texture everywhere, so don't worry, we will replace them. We will keep walls flat but pillars will be more complex than now.

For now I’m implementing level editor which generates entire level in 3D world, we choose the size of the individual components and see how it all looks. Final models will come later.

Baked ambient occlusion is a nice idea, but in this case (dungeons are made of blocks, they are not static) it’s a bit hard to do. Of course we will try to make some tricks.

Some simple AO is not too hard even with blocks. At least at the bottoms and tops of the walls. I did it in Monkey Trap even on the floor and it doesn’t look too bad even if the floor AO is wrong in places.

I look forward to seeing what you do.