Face Normals and odd lighting glitch

lighting

#1

Hi Everyone,
Please view this video I uploaded:

I recorded an odd lighting issue that I was hoping someone could shed light on.

What I’m doing; In the video I have displayed are a handful of the 2000 people I have simulated moving around a planet. The people are made of very few vertices and faces. They look blurry because I’m zoomed out quite a bit and vid quality. In order to save or processing power I’ve clumped all the people into one geometry that is dynamically updated every frame. The geometry’s vertices are then updated each frame. The geometry is only made up of those people who are visible to the user. All other people are not rendered but their position is updated every frame.

As you can see the lighting seems to ‘flicker’,
At the end of my dynamic geometry updating function I run the following:

simX.geometry.verticesNeedUpdate = true;
simX.geometry.facesNeedUpdate = true;
simX.geometry.computeFaceNormals();
simX.geometry.computeBoundingSphere();
simX.updateMatrixWorld();

Each person is individually select-able, so I need to run .computeFaceNormals and .computeBoundingSphere each frame.

But why the flicker and how can I get rid of it?


#2

Any chances you create a fiddle for debugging purposes with the relevant parts of your app? Without seeing code, it’s hard to say what’s going wrong.


#3

Well actually, here are the two functions which need analysing. Everything else is out of the box provided by three.js.

function animate() {
	FPSstats.begin();

	requestAnimationFrame( animate );
	checkKeyboard();

	if( _loaded ){ testMoveActor();	}

	renderer.render( scene, camera );
	rendererStats.update(renderer);

	FPSstats.end();				
}

function testMoveActor(){
	
	//console.time( '::Simulin Time::' );
	_bv = 0;
	_a=0, _b=0;

	for( var i=0, ii=_si.length; i<ii; i++ ){

		_sii = _si[ i ];

		// - IS ACTOR MOVING?
		if( _sii.moving ){
			// - YES, actor is moving

			_sii.cStep ++;

			// -- ARE SMALL STEPS DONE?
			if( _sii.cStep == _sii.steps[ _sii.step ] ){
				// - YES, actor is moving
				// -- YES, small steps equals total small steps to be taken.

				_sii.step ++;
				
				// --- ARE THE LARGE STEPS DONE?
				if( _sii.step == _sii.steps.length ){
					// - YES, actor is moving
					// -- YES, small steps equals total small steps to be taken.
					// --- YES, large steps are done

					if( _sii.human ){
						_sii.moving = false;
						_sii.step --;
						_stp = _sii.step;
					} else {
						_sii.step = 0;
						_sii.cStep = 0;
						_stp = _sii.step;
					}
		

				} else {
					// - YES, actor is moving
					// -- YES, small steps equals total small steps to be taken.
					// --- NO, large steps are not done
					_sii.cStep = 0;
					_stp = _sii.step;

				}

			} else {
				// - YES, actor is moving
				// -- NO, more small steps need to be taken

				_stp = _sii.step;
				
			}

		} else {

			// - NO, Actor is not moving.
			_stp = _sii.step;
			
		}

		// ---- IS GROUP VISIBLE?
		if( globalMesh[ _sii.group[ _stp ] ].visible ){

			_A[ _a ] = i;
			_a++;

		}	

	}

	_smv = simX.geometry.vertices;

	for( var i=0; i<_a; i++ ){

		_sii = _si[ _A[ i ] ];
		_stp = _sii.step;

		if( _sii.cStep == 0 ){
			_sii.iX = _sii.int[ _stp ].x;
			_sii.iY = _sii.int[ _stp ].y;
			_sii.iZ = _sii.int[ _stp ].z;
			_sii.lF = _sii.lfactor[ _stp ];
			
			_v2 = _sii.path[ _stp ].clone();
			_v3.set( _v2.x + _sii.iX , _v2.y + _sii.iY , _v2.z + _sii.iZ );
			_v3.multiplyScalar( _sii.lF );

			_sii.position.set( _sii.path[ _stp ].x + ( _sii.iX * _sii.cStep ) , _sii.path[ _stp ].y + ( _sii.iY * _sii.cStep ) , _sii.path[ _stp ].z + ( _sii.iZ * _sii.cStep ) );
			_sii.up = _sii.position.clone();
			_sii.lookAt( _v3 );
			_sii.updateMatrixWorld();

			for( var a=0; a<35; a++ ){
				_qv = _sii.geometry.vertices[a].clone().applyMatrix4( _sii.matrixWorld );
				_smv[ _bv ].set( _qv.x , _qv.y , _qv.z );
				_smv[ _bv ].ID = _sii.ID;
				_bv ++;
			}
		} else {
			_sii.iX = _sii.int[ _stp ].x;
			_sii.iY = _sii.int[ _stp ].y;
			_sii.iZ = _sii.int[ _stp ].z;
			_sii.lF = _sii.lfactor[ _stp ];

			_sii.position.set( _sii.path[ _stp ].x + ( _sii.iX * _sii.cStep ) , _sii.path[ _stp ].y + ( _sii.iY * _sii.cStep ) , _sii.path[ _stp ].z + ( _sii.iZ * _sii.cStep ) );
			_sii.up = _sii.position.clone();
			_sii.updateMatrixWorld();

			for( var a=0; a<35; a++ ){
				_qv = _sii.geometry.vertices[a].clone().applyMatrix4( _sii.matrixWorld );
				_qv.ID = _sii.ID;
				_smv[ _bv ].set( _qv.x , _qv.y , _qv.z );
				_smv[ _bv ].ID = _sii.ID;
				_bv ++;
			}
		}
	}

	for( var i = _bv, ii=_smv.length; i<ii; i++ ){
		_smv[ i ].set( 0,0,0 );
	}

	simX.geometry.verticesNeedUpdate = true;
	simX.geometry.facesNeedUpdate = true;
	simX.geometry.computeFaceNormals();
	simX.geometry.computeBoundingSphere();
	simX.updateMatrixWorld();
	simX.visible = true;
}

I apologise for the poor variables. Any thoughts? Any ideas on how to improve it in other ways haha?

If you still need me to make a fiddle I can try. I’m just worried my limited sample won’t duplicate the effect.


#4

That’s kind of an understatement. Were they generated by a machine or something?

I agree with @Mugen87 here - in order to get help with this, or even to figure it out on your own, you need to create a simple and complete example that demonstrates the problem - say, just 3 or 4 people on a simple plane geometry.


#5

Eeek, that bad hey… Surprisingly there is a tiny bit of logic behind it… :confused:

Hmm, well one thing I will quickly mention, I just ran a test. I noticed a pattern. The Geometry is made of 70,000 vertices and 104,000 faces. The vertices are clumped in groups of 35 and then 52 faces are assigned to each set of 35 vertices. So if all 2000 people are on screen at the same time then the geometry displays all sets. however if only 1000 are visible then the geometry only assigns the vertices positions for the 1000 people and then the remainder vertices are collapsed to x,y,z of 0,0,0. Thus not visible.

I ran a test to determine when the odd glitch happens. It happens everytime the number of people to be displayed changes. So if there are 900 people visible, then the next frame there are 100 people visible (because people are constantly moving, including out of view) the odd lighting thing happens. Just because it looks like the same person is moving along the surface of the planet, every frame their may be a different set of vertices and faces representing it.

Does collapsing the remaining vertices to 0,0,0 cause issues with face normal calculations?
Also, I’m under the impression that faceNormal updates are done every frame, is this true?
One last thing, ’ Frustum culled’ means, I had to do that too.


#6

Well, in my opinion anyway, the only logic that there should be behind variable names is that you should be able to show your code to anybody, without comments, and they should be able to read it.

You’re gonna minify and gzip the code in production anyway, so there is no reason you need to keep variable names short. Be descriptive.


#7

Can I see your camera settings, could be z-fighting? Does it go away if you zoom in on an individual?


#8

Hi thanks for the idea. I googled z-fighting and am familiar with its effect. In this case it’s not z-fighting.
It seems as though the the face normals are being swapped out, at least that is what I observe. So on one part of the planet they may appear correctly, but then the face normal quickly gets swapped out to the face normals of another area. I have the facenormals and vertices positions set to update every frame though, so I thought this would have solved the problem. But something else is going on. Hrmm. I might have to make a fiddle that, uses good variable declarations, to move forward on this one. I thought maybe someone familiar would be able to recall something, kinda like what you did.

[ edit: another thing too I am noticing is that the normals are wrong as well. Some people will have dark faces facing the light source, others will have light faces facing the light source. It’s almost as thought the facenormals are inverted in some places for some reason, just guessing ]


#9

ah shucks, so simple
I was missing
simX.geometry.normalsNeedUpdate = true;

Problem solved.


#10

Lots of text here lots of code, add ambiguous names and boom people won’t have time to read it :slight_smile:

Stack overflow has a good page on MCVE.

The problem

Imagine if 1000 people came here, each with a thousand lines of code, whole apps with mangled variable names. A lot of time would have to be invested to answer all those questions and they wouldn’t be useful to anyone else.

If you abstract and isolate your problem, it may be the same problem 500 people out of that group are facing, when I answer yours I answer 500 others which is efficient.

On SO people probably wouldn’t even read this, at most you’de get hi, welcome to SO, please learn how to ask a question

One last thing, ’ Frustum culled’ means, I had to do that too.

Like this for example, is actually well documented:
https://threejs.org/docs/#api/core/Object3D.frustumCulled

Also, I’m under the impression that faceNormal updates are done every frame, is this true?

You can simply put a break point in a function that you’re interested like computeFaceNormal() and see if it does happen or not. If you’re less experienced and can’t do that, one could perhaps deduce from the docs and examples that this is something you call manually.

https://threejs.org/docs/#api/core/Geometry.computeFaceNormals

It may not be super obvious, but it’s fairly straightforward. If these were happening every frame without you knowing, they wouldn’t be documented there.

It doesn’t, it would be super inefficient.


#11

Also, this is not a very good approach though. Have you benchmarked this? It seems like you actually introduce a lot of processing, and stall the graphics pipeline. Updating all the vertices, all the faces, all the normals etc, you might as well just skip using webgl. What is the logic to your approach here?


#12

Huh, interesting. Well admittedly I’ve just done that which was necessary for a desired outcome.
A little background; I started using THREE.js back at the beginning of 2014 because it was extremely easy to create 3D content. I also liked that it was in a browser. I don’t have any formal education in programming, other than what tutorials I’ve followed and what I’ve managed to figure out over the years.
I’m trying to make a game. The game is a sim-civ style game, or so I am hoping. Currently there is a small planet with simple trees and shrubs, mountains and rivers. On the planet there are 2000 moving people, each person is individually select-able.

Problem: if I show each person as thier own Mesh then every frame there are thousands of draw-calls and that slows down my fps to about 2. But If I treat them all as one mesh then I can maintain a decent fps. ~60.


#13

Speaking of z-fighting. Is there an easy fix to this problem? Currently I’m clipping the geometry so that faces don’t overlap.