Particles

Hxaria, Infinite Terrain [HaXe, WebGL,dat.GUI]

So I have been working on my “Terraria like Terrain” project “Hxaria” again.

Following on from the last post, I have now made it so that each particle can have its texture changed. This completes the functionality required to render each tile as a point sprite, as talked about in my previous post.

The way it works is that the entire world is recorded in a 2×2 array Tilemap. This 2×2 array holds a single Tile object for every single tile in the world:

  1. class Tile
  2. {
  3. public var x : Int;
  4. public var y : Int;
  5. public var type : Int;
  6.  
  7. public function new(x:Int, y:Int, type:Int) { this.x = x; this.y = y; this.type = type; }
  8. }

 

When the TileRenderer needs to render it asks this Tilemap for a Tile that represents that screen coordinate, the Tilemap then offsets the position due to the camera movement and returns a tile. So it looks something like:

The tile type is then passed to the shader in attribute buffers per point sprite / tile along with all the tiles which are stored on a single texture:

The shader then performs the neccessary calculations to work out what the UV coordinate in the texture. The Vertex Shader:

  1. uniform float amplitude;
  2. uniform float tileSize;
  3. uniform float texTilesWide;
  4. uniform float texTilesHigh;
  5. uniform float invTexTilesWide;
  6. uniform float invTexTilesHigh;
  7.  
  8. attribute float size;
  9. attribute vec3 customColor;
  10. attribute float tileType;
  11.  
  12. varying vec3 vColor;
  13. varying vec2 vTilePos;
  14.  
  15. void main()
  16. {
  17. vColor = customColor;
  18.  
  19. float t = floor(tileType/texTilesWide);
  20. vTilePos = vec2(tileType-(t*texTilesWide), t); // +(.5/tileSize)
  21.  
  22. gl_PointSize = size;
  23. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  24. }

 

And the Fragment Shader:

  1. uniform vec3 color;
  2. uniform sampler2D texture;
  3. uniform float invTexTilesWide;
  4. uniform float invTexTilesHigh;
  5.  
  6. varying vec3 vColor;
  7. varying vec2 vTilePos;
  8.  
  9. void main()
  10. {
  11. vec2 uv = vec2( gl_PointCoord.x*invTexTilesWide + invTexTilesWide*vTilePos.x, gl_PointCoord.y*invTexTilesHigh + invTexTilesHigh*vTilePos.y);
  12.  
  13. gl_FragColor = texture2D( texture, uv );
  14. }

So it works in a way very much like a raster engine. You only have to render as many particles as the screen can contain.

If the screen area moves beyond the extent of the Tilemap then more tiles are randomly generated:

The new tiles are randomly selected from 4 different types, Dirt, Gold, Diamonds and Rock. I have added some controls to the demo that allow you to tweak these values to demonstrate the random tile generation:

The UI may look familiar to people that have seen any experiments anyone who has worked with Three.js before, its the very popular dat.GUI. Its a really simple library written in javascript for creating controls that can be used to tweak experiments, perfect for me!

To get dat.GUI to work with haxe, I used the awesome Extern feature of HaXe. This means that all I have to do is provide a stub interface to dat.GUI rather than a full implementation in haXe. This is great as it allows me to rapidly begin to use the library but also have the type safety of HaXe. It didnt take long to stub out the bits of the library I needed in an extern:

  1. package dat;
  2.  
  3. /**
  4.  * ...
  5.  * @author Mike Cann
  6.  */
  7.  
  8. extern class GUI
  9. {
  10.  
  11. public function new(options:Dynamic) : Void;
  12. public function add(options:Dynamic, name:String) : GUI;
  13. public function name(value:String) : GUI;
  14. public function min(value:Float) : GUI;
  15. public function max(value:Float) : GUI;
  16. public function step(value:Float) : GUI;
  17. public function onFinishChange(f:Void -> Void) : GUI;
  18. public function listen() : GUI;
  19. }

Then I used it like:

package ;
import dat.GUI;
 
/**
 * ...
 * @author
 */
 
class GUIManager
{
	public var goldChance : Float;
	public var rockChance : Float;
	public var diamondsChance : Float;
	public var mapWidth : Int;
	public var mapHeight : Int;
 
	private var gui : GUI;
	private var game : Game;
 
	public function new(game:Game)
	{
		this.game = game;
 
		gui = new GUI( { height : 5 * 32 - 1 } );
 
		goldChance = game.tilemap.goldSpawnChance;
		rockChance = game.tilemap.rockSpawnChance;
		diamondsChance = game.tilemap.diamondsSpawnChance;
		game.tilemap.mapResized = onTilemapResized;
		mapWidth = 0;
		mapHeight = 0;
 
		gui.add(this, 'goldChance').name("Gold").min(0).max(1).step(0.001).onFinishChange(function() { game.tilemap.goldSpawnChance = goldChance; } );
		gui.add(this, 'rockChance').name("Rock").min(0).max(1).step(0.001).onFinishChange(function() { game.tilemap.rockSpawnChance = rockChance; } );
		gui.add(this, 'diamondsChance').name("Diamond").min(0).max(1).step(0.001).onFinishChange(function() { game.tilemap.diamondsSpawnChance = diamondsChance; } );
		gui.add(this, 'mapWidth').listen();
		gui.add(this, 'mapHeight').listen();
	}
 
	private function onTilemapResized(mapW:Int, mapH:Int):Void
	{
		mapWidth = mapW;
		mapHeight = mapH;
	}
}

Simples!

Anyways you can check the final result out on this page: http://mikecann.co.uk/projects/hxaria/02/
(Click and drag to move the camera about)

I have also uploaded a quick video too:

I have also uploaded the source again to my github page: https://github.com/mikecann/Hxaria
(I have also created a tag, incase the source changes in the future)

Next up, lighting!

GPU State Preserving Particle Systems with WebGL & HaXe

Well this is the post I didnt think was going to happen. I have been struggling for weeks with this little bit of tech, ill explain more about why it has been so difficult in another post. For now however, ill just talk about this sample.

So the idea was to build upon what I had been working with previously with my stateless particles systems with WebGL and HaXe. The intention from the start was to replicate some of my very early work (from 2007) on state preserving particle systems in WebGL.

Before I go any further, you can check it out in action here:
http://mikecann.co.uk/projects/HaxeWebGLParticles/ 

First a quick reminder. The difference between a stateless and state-preserving particle simulation is that in the latter we store and update the positions, velocities and other properties of each particle per frame, allowing us to interact and control the simulation. This differs from the stateless particle simulation (detailed in my previous post), where the position for each particle is calculated each frame based on a fixed algorithm.

A fairly reccent addition to WebGL made this possible, namely texture lookups in the vertex shader (aka Vertex Texture Fetch). I wont go over the exact details how this makes state preserving particle systems possible as I have already documented it in my earlier work. A brief explanation is that it allows you to use the fragment shader to perform the updates on particle state stored in textures then use the vertex shader to map those states to a point-sprite vertex buffer.

Basically what this means is that the entire particle simulation can be contained and updated on the GPU, which means no read-back. This allows us to achieve simulations of millions of particles without too much difficulty (depending on GPU ofcourse).

I have uploaded the source for people to perouse at their leisure:
https://github.com/mikecann/HaxeWebGLParticles

As usual it was written using the JS target of HaXe so it should be fairly easy to understand whats going on if you have written any Ecma-script-like-language. Im going to detail this in my next post, but the code isnt the best I have ever written as its a result of a mish-mash of various samples and examples I have found on the web. If anyone has any comments on things that are wrong or could be done better I would be very happy to hear about them.

5,000,000 Chrome Crawlers? Why not [haXe & WebGL]

Following on from my previous experiments into the world of haXe and HTML5 I have been playing around again with trying to get as many 2D sprites on screen as I can.

I started by reading some posts by google on how to render things fast in HTML5, and it got me thinking. Where I was likely going wrong with my HaXe + Three.js experiments was that I was making a separate draw call to WebGL for each and every crawler. Draw calls are expensive and hence I was reaching the draw call bottleneck at just 2000 sprites being rendered at once.

What I needed to do was batch the draw calls together and render them at once. I knew from my work on XNA you could group sprite render calls together quite nicely. So I started off coding up a WebGL equivillant of SpriteBatch from XNA.  I managed to get it kind-of working but as is often the way another thought struck my brain, and I decided to tackle that instead.

My thought was; why not just render everything as 2D point sprites? I remembered from my XNA days you could achieve quite staggering numbers of 2D sprites in DirectX by using point spites.

So after a little bit of fiddling and hacking I managed to get point sprites correctly rendering:

You can play with it here: http://mikecann.co.uk/projects/HTML5SpeedTests/HaXeWebGL/bin/

The great thing about point sprites is that I only use one draw call per frame and the GPU is very good at rendering them. The only bottleneck really is the number of pixels you need to draw. With that in mind if you drop the size of the point sprite down to 1×1 you can render a very large (5million) points at interactive framerates (18fps):

I added a “dont use texture” option just out of interest to see how expensive the texture lookup in the fragment shader was, it didnt seem have much of an effect.

There are a few caveats to using point sprites:

Firstly in WebGL has a limit on how large you can make them currently it differs between graphics cards and browsers, a safe minimum is 64×64 so this means you cant have them and bigger and still want it to work in all situations.

Secondly, and this one is more important I cheated to get the numbers above. In my other experiments with haXe and WebGL I was using the CPU to update the positions of the crawlers each frame, having them bounce off the screen edges. In this point sprites demo however I have the points flowing out of a fountain, the simulation of which is entirely calculated on the GPU. The reason for this I talked about in a paper I wrote for a university project 4 years ago:

If I wasn’t to perform the upates on the GPU but instead just use the CPU to update the crawlers that would mean the javascript (CPU) would need to update 5million crawlers each frame then re-upload the point sprite positions back to the GPU for rendering. This would obviously be a bad idea.

So I kind of cheated. The fountain simulation on the GPU isn’t the same as my other examples. The crawlers don’t bounce off the side of the screen. To make that that happen each crawler needs to somehow preserve its state between frames.

Currently each crawler’s position is calculated in the vertex shader each frame like so:

attribute float maxAge;
attribute float startingAge;
attribute float velX;
attribute float velY;
 
uniform float uTime;
uniform float uPointSize;
 
varying float invAgeRatioSq;
 
void main(void)
{
	float age = mod(uTime+startingAge, maxAge);
	float ageRatio = age / maxAge;
	float invAge = 1.0 - ageRatio;
	invAgeRatioSq = 1.0 - (ageRatio * ageRatio);
 
	gl_Position = vec4((-velX*ageRatio*0.8), (velY*ageRatio)+(-0.4*age*ageRatio)-0.5, 0., 1.);
 
	gl_PointSize = uPointSize;
}

To preserve state between frames I need to use textures to record each crawlers position and velocity, then using Vertex Texture Fetch a vertices position can be calculated.

That however will have to wait for another evening ;)

I have uploaded the source for this project here incase anyone was interested:

http://mikecann.co.uk/projects/HTML5SpeedTests/HTML5SpeedTests_2.zip

I warn you its ugly and uncommented, however it should be enough of a start for anyone looking to do something similar.

1 2 3 4  Scroll to top