I am building a tile game using Pixi.js. The game features dynamically-generated terrain and a day/night cycle. I'd like to use a normal map to give the sense of sloping hills or mountains.

Luckily pixi.js supports normals. I've recreated a demo app from their examples here:

var viewWidth = 1024 // /2;
	var viewHeight = 1024 //512 /2;

	 // Create a pixi renderer
	var renderer = PIXI.autoDetectRenderer(viewWidth, viewHeight);
	renderer.view.className = "rendererView";

	 // add render view to DOM

	 // create an new instance of a pixi stage
	var stage = new PIXI.Stage(0xFFFFFF);

	 //EXPERIMENTAL: Trying to dynamically create textures. Doesn't work.
	 //If using this method, comment out line 24.
	/*var rect = new PIXI.Graphics();
	var pondFloorTexture = rect.generateTexture();*/

	 // create a background texture
	 var pondFloorTexture = PIXI.Texture.fromImage("http://www.goodboydigital.com/pixijs/examples/20/counch_norm.jpg");

	 // create a new background sprite
	 //var pondFloorSprite = new PIXI.Sprite(pondFloorTexture);

	var filter = new PIXI.NormalMapFilter(pondFloorTexture);

	var sprite = PIXI.Sprite.fromImage("http://www.goodboydigital.com/pixijs/examples/20/couch.jpg"); //(pondFloorTexture);

	sprite.filters = [filter];

	var tick = 0;

	function animate() {

	  // increment the ticker
	  tick += 0.1;

	  var mouse = stage.interactionManager.mouse;

	  if (mouse.global.x < 0) mouse.global.x = 0;
	  else if (mouse.global.x > viewWidth) mouse.global.x = viewWidth;

	  if (mouse.global.y < 0) mouse.global.y = 0;
	  else if (mouse.global.y > viewHeight) mouse.global.y = viewHeight;

	  filter.uniforms.LightPos.value[0] = mouse.global.x;
	  filter.uniforms.LightPos.value[1] = mouse.global.y;
	  // time to render the state!

	  // request another animation frame..
<script src="http://www.goodboydigital.com/pixijs/examples/20/pixi.js"></script>
<script src="http://www.goodboydigital.com/pixijs/examples/20/NormalMapFilter.js"></script>

Note that at line 15 I've added a bit of code that attempts to replace the normal map with a dynamically created rectangle. But for some reason this results in everything appearing black. Am I doing something wrong? Is there a better way to do this?

But more importantly, should I be creating normal maps dynamically? Obviously generating them on the fly will be computationally expensive, but how else would I get a normal map for something I won't know the shape of until last minute?


I'm confused by your question; I think you're going about this all wrong.

Let's first consider that it would have to be an advanced algorithm indeed that could (accurately) reconstruct surface normals given no more than a texture implying surface geometry.

If I had to tackle that problem myself, I'd try getting at it by treating every pixel as a height within a heightmap. But there would be all kinds of factors required to determine a pixel's actual height from its shading, everything from it's albedo colour to whether it is in shade due to overhangs. Non-trivial. Then you'd need to use the heightmap to determine surface gradients and thus normals at various points. I've seen similar problems solved... by computational geometry specialists in doctors' level theses. I'm not even sure this probably could actually be solved for all cases, because an object could have albedo colours that confuse the geometry reconstruction algorithm (most notably: full black).

So back down to planet Earth: if you're dynamically generating the terrain, you should be extracting spatial data during generation, then implementing proper lighting therewith, whether via normal maps or any other approach.


Apparently the NormalMapFilter extension is very limited and doesn't even work with Pixi.js v3. NormalMapFilter might just be buggy and may never be fixed. Instead, the preferred method of lighting and normal maps in Pixi.js is currently to use the pixi-lights extension.

Now I've played around with it, and I'm still having some serious problems with texture swapping when including pixi-lights with my project (I'm almost certain this is a bug with the library since it breaks my code just by being included, but I will report it or suggest a fix once I discover why it breaks texture swapping in my application). Nevertheless, I expect that this extension will not be limited by the quirks of NormalMapFliter.

As for dynamically creating normal maps, I've decided that while it may be possible, there is a better way. I'll just create a simple program to create several variations of a normal map in advance and include them in the texture atlas. Without getting into specifics, the normal map for a tile that is facing east then might look like this:


The normal map for a tile on a cliff facing south-west and plateaus might look like this:


And so on. So that I'll have a relatively small number of these to choose from. Suppose that each of the four sides has three possibilities: slope up, slope down, or plateau. Then there are at most 3x3x3x3 = 81 of these to generate - many of them being mirror images of each other (but you get the idea).


The purpose of normal maps is to contain some detail that is used to calculate how much light will contribute lighting up parts of a polygon.

If all your tiles are flat squares there isnt a need of a normal map. Just apply the inverse transform matrix of your modelview.

In your case you would probably need to calculate the normals of the entire mesh terrain. This would give you a position and normal per vertex. If the terrain doesnt rotate or scale then there is also no need to apply any matrix to the vertices


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .