The forum is read-only. New threads, replies and edits are disabled.
Pixel shader - have a need to reuse
coal

Two days ago I discovеred this pretty framework Wade. It's awesome and allows to do much things.Here some my examples from simple to complex

http://seganelservice.ru/WebGL/Wade1

http://seganelservice.ru/WebGL/Wade2

http://seganelservice.ru/WebGL/Wade4

http://seganelservice.ru/WebGL/Wade5

http://seganelservice.ru/WebGL/Wade6

http://seganelservice.ru/WebGL/Wade7

 

1. In all examples I hardly used custom pixel shaders for animation. And now I want to reuse shaders, save them as project file, as javascript file in project and apply files to another sprites. But now there is no option... There is real need to do that - save shader content as file with uniforms, and reuse it on other sprites by aplying to sprites shaders list.

Also, multishaders (multipass) on one sprite and on postprocess - will be very useful for some multipass techniques as bluring and another complex effects.

Also, it seems that postprocess on scene doesn't work on sprites with custom shader. May be I'm wrong.

2. There is some problem to deploy source code on server (seganelservice.ru) by copy source code to folder. As is - server show error 404. You can found it on browser console at address http://seganelservice.ru/WebGL/WadeError/ where I simply copied source code, downloaded from Wade.

Errors from console:

/WebGL/WadeError/scene1_night_glows_posteffect_hard_rain.wsc:1 Failed to load resource: the server responded with a status of 404 (Not Found)
wade.js:469 Failed to load JSON data from scene1_night_glows_posteffect_hard_rain.wsc : SyntaxError: Unexpected token < in JSON at position 0
Wade.error @ wade.js:469
wade.js:469 Unable to load json file scene1_night_glows_posteffect_hard_rain.wsc

I solved this problem by renaming scene1_night_glows_posteffect_hard_rain.wsc то *.js and by patching app.js. But error is strange, could you help me - how to solve it right?

All 71 Comments
Gio

Hi coal and welcome to the forum

Very nice stuff there, I really like the stuff you've done so far - the last one in particular is looking great.

Regarding pixel shaders:

Wade knows when you are re-using the same pixel shaders. If you have two sprites that use the same shader code, Wade will notice and will not create two different copies of the shader - both sprites will end up using the same one. It does this by comparing the hash of the string that you use for the shader code. So even when you think you're duplicating the same code, in fact it is only one shader.

You can also store a shader in a text file, and use Sprite.setPixelShader() to apply it to your sprites.

When I'm using the editor and need to re-use the same shader across different sprites, first I normally create a sprite with my shader (let's call it refSprite). Then, for all the sprites that need the same shader, I call

mySprite.setPixelShader(refSprite.getPixelShader, refSprite.getPixelShaderUniforms)

If you're not using the editor but just the framework, you could do something similar by storing your shaders in a text file and using wade.loadText, or even better store them in a JSON file where you can store both the shader code and the uniforms with their values, and then use wade.loadJSON.

Multi-pass shaders are a complex subject in general - I haven't really figured out how we can do that without making it too complex for Wade users to put all the pieces together. Currently you can do multi-pass shaders by using multiple sprites, one for each pass, and drawing to textures. Not ideal I know, but it works:

firstSprite.drawToTexture('tempTexture');
secondSprite.inputTexture = 'tempTexture';

If you do that, then in the secondSprite's pixel shader you can use a Sampler2D uniform (called 'inputTexture' in this case) that contains the result of the firstSprite's draw. I hope this makes sense.

I realize that this is a bit complicated, and that's precisely why we have a built-in multi-pass shader for blur, that is the most common case.

You can set the blur level for each layer using wade.setBlur (see here for an example). You can also use the blurred texture of each layer (which is only available when blur is set to something > 0), using a Sampler2D uniform whose value is _layerBlur_X (where X is the layer id). Similarly, you can access each layer's render target (i.e. the normal, non-blurred texture) as _layerRenderTarget_X.

That's pretty advanced stuff, so let me know if you get stuck and need help with it, I'll try to help.

Regarding your second point: I think this must be some configuration problem with your server... it seems it doesn't let you upload (or download)  files with a wsc extension. Are you sure they exist on the server, i.e. they have been successfully uploaded? I think there should be something you can change in the config to stop blocking them. This will depend on what type of server you are using though.

coal

>If you're not using the editor but just the framework,

I use editor currently, because I'm newbie in js, I must work out much js code examples to understand architecture and copy patterns. But soonly I'll migrate to clean framework.

Thank's a lot, i'll try. You've made a great work with editor!

coal

No possibility to set shader on SceneObject. My scenario:

1. Load tree.png

2. Make multisprite SceneObject with 1000 trees (sprites) in random positions by js code

3. Apply shader to SceneObject (to all 1000 trees. For example apply day/night colors, dynamic lightning, scroll texture, etc)

I want to make smth like this http://seganelservice.ru/WebGL/Wade11/ (it's only test, not final scene). But I want to do this from js with procedural forest generation and fast execution performance (currently there is very big texture of 3000*2000, which is too slow in shader scrolling. I know, that I can resize texture in PS, but also there is no random currently in it).

Is there another ways to realize this scenario (make forest randomly from js with one tree picture, and apply shader to all forest)?

 

Here I've generated such random forest from js - one SceneObject, many sprites on it. But now I can't scroll it, and apply any shader to all SceneObject.

Forest generator by click, but with no scroll (((

 

 

 

It will be 3-4 such SceneObjects in different layers on scene, each layer with different scroll speed (3D emulation as in demos). Question is about performace optimization only - not to scroll 150 sprites, but scroll one SceneObject texture in one shader.

coal

I can't ask right question, because right question has 90% of answer... But I want glue many (100/500/1000) textures programmatically in one/two/three textures screen-size, for performance. And animate them by one/two/three shader, not 100/500/1000 shaders or js position cycle. I'm afraid that many spriteobjects in cycle will be slow (I saw it on phone, while opening some my demos in browser). I want to understand wade pipeline - how sprites send to gpu in framework and what is optimal case...  Currently all is fast, but I'm afraid compex scenes may be slow... What is the fastest way to animate 10000 sprites - is there any doc or topic on forum?

Gio

Hi

First of all let's try to understand what you mean by "animate".

When you have many sprites using the same texture and the default shader (it doesn't matter if they belong to the same scene object or not), Wade will draw them in batches. This  means that if you have 10000 sprites Wade will only do about 10 WebGL draw calls. Add to this the fact that sprites that are outside the screen are not drawn at all, so it's super fast.

However if you use your own custom shader, it's one draw call for each sprite so 10000 draw calls - quite a bit slower.

So it's important to understand if and why you need a custom shader. If it's an effect that can be done in post process, then using the default shader for your sprites and a custom post process shader on the layer is almost certainly going to be faster.

If you can slow me what effect you're trying to achieve I may be able to help some more 

coal

Thanks a lot, I understand idea about postprocessing. I tried it and it almost works, here code of postprocessing of forest layer (endless texture scroll to imitate scene movement). Trees wind-animated while scrolling, almost all is awesome, except invisible moon on other layer (but may be it's my fault, I examine this case later)

vec2 uv = uvAlphaTime.xy;
uv.x+=uvAlphaTime.w/1.0;
uv.x=fract(uv.x);
vec4 color = texture2D(uDiffuseSampler, uv);
color.w=uvAlphaTime.z;
gl_FragColor = color;

But I found in other place main overhead, that impacts performance.

1. Generate 150 sprites in one SceneObject. One tree.gif is about 40Kb.

2. Set to each sprite "wind animate shader"

vec2 uv = uvAlphaTime.xy;

uv.x+=(1.0-uvAlphaTime.y)*sin(posx/500.0+ posy/500.0+sqrt(uvAlphaTime.y)+uvAlphaTime.w/3.0)/0.3)/10.0;

uv.x=fract(uv.x/1.0);
uv.x=fract(uv.x);

vec4 color = texture2D(uDiffuseSampler, uv);

if (color.b>0.86){    color.w=0.;}
color.r=1.0-color.r;
color.g=1.0-color.g;
color.b=1.0-color.b;
color.w *= uvAlphaTime.z;

gl_FragColor=color;

3. If I set sprite.alwaysDraw(false); then animation not works and GPU load 15% (why? there is no any animation, no position change, static screen).

If I set sprite.alwaysDraw(true); than animation works and GPU load is 86%(!), as in AAA+ games.

------------------------------------------------------------

I'm not very well at webgl, glsl and js, but I tried some times ago to work on my GPU with 4 500 000(!) particles with shaders. And with 200 000 geometries with shaders. And with 10000 meshes (3d models) with shaders.

 

But here we have only 150 trees (only textures, which are fastest way) and such GPU load... May be I'm doing smth wrong? Or may be such overload is because of CPU-GPU textures transitions on each update? I think that reason can be patched very fast in framework by some techniques, but I don't know it...

 

I strongly know that it can be faster at 10000 times, working with textured particles with threejs. May be, framework needs some patch? Or may be we need some new class? I have some performance realizations on threejs, for example million particles - and can upload them, if you need.

coal

Here I see 50% GPU load on FullHD, but there is less than 150 trees http://seganelservice.ru/WebGL/Wade15/

May be framework can be modified to give user option of redefine way to render/blend sprites? I'm not sure, it's assumption.

May be I need to disable QuadTree and all things in my case will become very fast and GPU load will become less?

Gio

Like I said above, if you don't use the default shader then your sprites are not batched. It's one draw call per sprite, which is far from ideal.

However, I don't think that's your problem there. The large amount of overdraw is likely to be the cause of the slow-down. That is, it's not the number of objects that makes things slow down, but rather the number of pixels that you are drawing.

GPU load in itself is not a very useful metric. Using a browser extension such as WebGL Inspector will give you more details about what exactly is affecting the performance of your scene. Trying to guess what slows things down is not a good idea - there are tools that will tell you for sure. From a quick look, I think it's most likely overdraw, but I may be wrong.

coal

1. Please, look here, http://seganelservice.ru/WebGL/OneMillionSprites/

- you can move camera and see 1 000 000 moving sprites. Not 150. No quad tree, no visibility check. Strictly and simple straight render with shaders.

- GPU load is 4-5%. Not 86%

- 8 FPS

2.  The same http://seganelservice.ru/WebGL/HundredThousandSprites/

- 100 000 moving sprites. 

- GPU 20%.

- FPS 60. No breaks.

This is single page code (index.html) + threejs + some common libraries.

Later, I make scene with trees on webgl+threejs and paste here link to compare performance.

coal

And here we can see 10000 particles with shaders, GPU 18%

http://oos.moxiecode.com/js_webgl/snowfall/

may be you'll like and realize such methods in framework.

Gio

You can already do that with Wade, there is no need to change anything to achieve that. I am suggesting that the main difference with the forest scene is the number of pixels that you are drawing, not the number of objects.

As stated above we do not currently batch sprites using custom shaders, which we could do and it would improve performance in some cases (but it still won't solve the problem with your trees, as it's probably unrelated). There is room for improvement there, but it's very low priority on the list of things to do.

coal

Gio, ok, last try... Strict test:

a) 100 000 small trees in wade. They are animated, but update (animation) hangs with 0.5FPS.

http://seganelservice.ru/WebGL/Wade16/

b) 100 000 big trees (more pixels) in threejs. Update is 40-60FPS.

No clipping! No culling! All 100 000 are animated with same custom shader. And the same scene/count/texture. And much more pixels. You can move camera and see it.

http://seganelservice.ru/WebGL/HundredThousandTrees/

 

You want to say that performance is low priority because all people use default shader... But shaders - are power in games... And performance affects ALL games of ALL users. It is core. Please, show me any wade project, wich display 100 000 animated sprites with shaders or with js. You can't show that.

But It is possible, you can see it on my demos. And it is possible to animate 1 000 000 sprites with no breaks. Pixels count doesn't affect. GPU renders only screen-pixels and nothing else at any frame. And optimal code is very simple, you can see it by "view source" in browser. It is not clean, but very very simple. And very fast.

This is core of framework, I think, not low priority. Currently wade is awesome in js, but very poor in performance. I tried 1500 sprites on snow emulation, but 10000 are hanging in wade. And 10000 snowflakes are flies in threejs...

Spend 3-4 hours - and you can make it much faster... At 100-500 times faster. And ALL users would can use more sprites and beatiful animation and effects.

coal

You can say - no need to show 100 000 sprites... But I saw problem on scrolling ONE big background sprite 4000*4000 with 150 animated trees only... And saw brakes... And saw brakes when scrolling texture in posteffect with 150 animated trees with shaders...

I can't make scene with 150 animated trees and scroll background moonlight - it moves with brakes... And I can't scroll texture with 150 trees, it moves with breaks. And any other animation is slower on such scene, human walks slower. And this is problem, please, hear me...

But solve is the same, as I said - in the core... Only few hours to solve. And all users will have possibility of beautiful shaders graphics.

coal

I think that you update mesh/geometry fully. And on each step textures are passed from CPU to GPU, which is bottleneck. Or, may be you update fully smth else...

But technique is to update only attributes/uniforms of shaders... And that is all animation magick.

 

This is how I update animation in threejs onRender.

a) uniform "time" in pixel shader

var elapsedMilliseconds = Date.now() - startTime;
var elapsedSeconds = elapsedMilliseconds / 1000.;
uniforms.time.value = elapsedSeconds;

b) attributes in vertex shader (positions/sizes of elephants/sprites):       

                    var pos = geometry.attributes.position.array;
                    for (var i = 0; i < particles; i++) {

                        UnitAI(i);

                        pos[i * 3] += units[i].deltax;
                        pos[i * 3 + 2] += units[i].deltay;

                    }
                    geometry.attributes.position.needsUpdate = true;

           very simple and very fast. And no any other updates. Pipeline CPU-GPU is bottleneck, extremly when updating textures...

Gio

Thanks for the suggestion.

Just to clarify, textures are not passed from CPU to GPU on each step, I'm not sure why you think that is the case.

Please understand that this engine / framework is designed to make it easy to make games and web apps. I don't think that letting users interact with vertex shader attributes or changing the rendering pipeline fits with that philosophy.

There will always be some special cases (like drawing thousands of objects with custom shaders), where it isn't as fast as it could be. Luckily though, it is not an issue for 99.99% of games. Batching sprites using the default shaders makes it very fast in the vast majority of real-world scenarios, and keeps the interface very simple. It is not worth making the interface more complicated for a few special cases. Most users do not want to mess with shader attributes, they just want to make games that work in a short time.

However when you want to make something special and this is an issue you have the source code that you can change to fit your specific needs. If, say, you're not happy with the way Sprite.draw works, there is nothing stopping you from redefining Sprite.draw in a way that works better for your project.

Having said that, when I can find a way of batching custom shaders without making the API more complicated to the detriment of the majority of users, I'll do it.

coal

>I'm not sure why you think that is the case.

I'm newbie in webgl and js ))) Only assumption...

>Sprite.draw works, there is nothing stopping you from redefining Sprite.draw in a way that works better for your project.

Thank you, I examine that!

>it is not an issue for 99.99% of games.

I want to explain my needs. I want to write my own game as Limbo

or Ori

or Gris

This is styled shaders games with many effects... I'm not designer, but I'm programmer. But webgl/glsl/js is only hobby and I don't know them well... 

So, I tried to write my own editor, but my knowledge in js is poor. And I found your excellent editor, which is all that I need, except shaders performance.

And I've made on it awesome forest. But I want more compex scene... So, I'll think how to found compromise and make such game with semi-procedural generaion without brakes.

Gio

I see what you mean, but I think in none of those examples above you have thousands of objects using custom shaders.

In all those cases you should be able to use lots of objects with the default shaders + a few objects with custom shaders + a post process shader.

Even in your case with trees - can't you just draw a lot of trees with the default shader in the background and add a few animated ones with a custom shader on top?

coal

>I see what you mean, but I think in none of those examples above you have thousands of objects using custom shaders.

Yes, of course, but what about nature effects? Rain? Snow? Day/night dynamic lightning? Stars effect? Wind? Thuderbolt? Moonlight glow? Shadered water? Waterfalls? Glowworms? Simple texture endless scroll? And many others... Which are rendered only with shaders... Animated backgrounds, live backgrounds - are the key to beatuful game if you're not designer...

Use prerendered textures? This is the way, but how to make them...

coal

In ideal I want to shader all (!) objects on screen. All are live and all are animated with own effects, except first plan... But I'll search compromise. If I found - I'll share it here )))

coal

That is why I can't use one postprocessing for wind animation. This is just illustration, just example (no need to answer)

http://seganelservice.ru/WebGL/Wade17/

Each tree has different y0, so, wind is impossible in postprocessing, only for each tree.

coal

 

This is draw function

Sprite.prototype.draw_gl = function(context)
{
    if (!context.isWebGl)
    {
        if (this.draw == Sprite.prototype.draw_gl)
        {
            this.draw = Sprite.prototype.draw_2d;
        }
        return this.draw_2d(context);
    }
    var image = anim && anim.getImage() || this._image;
    if (this._visible)
    {
        wade.numDrawCalls++;
        var anim = this._animations && this._animations[this._currentAnimation];
        this._f32RotationAlpha[1] = context.globalAlpha;
        if (context.globalCompositeOperation == 'lighter')
        {
            context.blendFuncSeparate(context.SRC_ALPHA, context.ONE, context.SRC_ALPHA, context.ONE);
        }
        var shaderProgram = this._shaderProgram || context.defaultShaderProgram;
        this._layer.setShaderProgram(shaderProgram);
        context.uniform4fv(shaderProgram.uniforms['uPositionAndSize'], this._f32PositionAndSize);
        context.uniform4fv(shaderProgram.uniforms['uAnimFrameInfo'], (anim && anim.getF32AnimFrameInfo() || this._f32AnimFrameInfo));
        context.uniform2fv(shaderProgram.uniforms['uRotationAlpha'], this._f32RotationAlpha);
        this._setPixelShaderUniforms(context, shaderProgram);
        context.setTextureImage(image);
        context.drawArrays(context.TRIANGLE_STRIP, 0, 4);
        if (context.globalCompositeOperation && context.globalCompositeOperation != 'sourceOver')
        {
            context.blendFuncSeparate(context.SRC_ALPHA, context.ONE_MINUS_SRC_ALPHA, context.ONE, context.ONE_MINUS_SRC_ALPHA);
        }
    }
    else
    {
        context.setTextureImage(image, true);
    }
};

I wonder - is there real need to reSet shader program on each redraw?

        this._layer.setShaderProgram(shaderProgram);

and also that code - that reSets texture (not sure).

context.setTextureImage(image);

I saw while debugging, that shaders are switched all time... Can you compare shader text and use shader from cache?

Also, may be try set texture through uniforms of this one shader? Only one time when change sprite image.

May be this is impact and this code is really needed only on init sprite or change its shader, not at any draw call? May be that is problem? I'm not sure, but I'll try to redefine function

Gio

this._layer.setShaderProgram(shaderProgram) doesn't set the shader if the current shader is the same as the one you're setting

coal

deleted

coal

I understand difference now. At my examples - there is only one geometry with vertex shader attribute arrays (size and position of particles, and texture as uniform). Length of array = particles number. Positions of each is rendered by vertex shader, by attributes.

So, I have only one draw call for all scene, because I have only one geometry with one texture. And it works fast. 

It is different technique - for sprite clones (particles). For my forest of 100000 trees I have only 5 draw calls because there is only 5 geometries with 5 textures...

Now I need to think how to extend framework to realize particles system by myself (waterfalls, snow, trees, clouds)

coal

To complete this task (class Particles) there must be done small modifications in framework:

1. vertex shader has no change

2. Change js initialization of uPositionAndSize from vec4 to array vec4:

var positionsAndSizes = new Float32Array(numberOfParticles * 4);

sprite.addAttribute('uPositionAndSize', new THREE.BufferAttribute(positionsAndSizes , 4));

this creates N clones of sprite automatically on GPU (may be here needed smth else - one can examine my elephant example with threejs)

3. Positions and sizes of individual particle clone are initialized by code (and can ve animated later by user - as for sprites):

var positionsAndSizes = geometry.attributes.positionsAndSizes .array;
for (var i = 0; i < numberOfParticles ; i++) {

positionsAndSizes [i * 4] = pos.x;
positionsAndSizes [i * 4 + 1] = pos.y;

positionsAndSizes [i * 4 + 2] = size.x;

positionsAndSizes [i * 4 + 3] = size.y;

}

4. User write pixel shader as usual. 

 

Result: there is N clones of sprite with same texture. Cloning is proceeded by GPU. Clones have different positions and size (vertex shader as is).  All the rest is done in custom pixel shader.

This N clones, for example 1 000 000, are drawn by only one Draw Call - which is extremly fast. And any clone can be displayed and transformed individual in pixel shader if user want - may have different color, have different wind reaction, where particle is tree for example (it needs to deliver uPositionAndSize from vertex to pixel shader, as done in varying vec4 uvAlphaTime; - and than user can analyze particle position on screen and indentify it on pixel shader, and transform individually)

---------------------

This is pretty simple to realize Particle system (clones of one texture with individual positions, sizes and transformations), but I can't do this myself. So, will gracefully wait implementaion of this, while examing other features of editor...

Such particles system will allow to make fantastic effects: clouds, real water, waterfall, rain, snow, fog, croud, stars, fireworks, explosions and many other things with no performance impact. And this effects will decorate 2D, not spoil.

Gio

That's what we are already doing for sprites that use the default shader. 

The problem with custom shaders is what to do with uniforms

coal

Also this method is best for tiling. In isometric map. And in 2d screen - to tile screen area by small texture. Very useful method...

>The problem with custom shaders is what to do with uniforms

Uniforms are added to pixel shader as currently, no difference and no change, I guess. I saw your code - and I use the same for elephants demo (clones). 

Elephants (particles) pixel shader:

uniform float time;
uniform vec3 color;
uniform sampler2D texture;
varying vec3 vColor;

            void main() {

	    vec2 uv = gl_PointCoord;
	    
	    uv.x+=(1.0-uv.y)*sin((sqrt(uv.y)+time/3.0)/0.3)/10.0;
	    uv.x=fract(uv.x);
		uv.y=1.0-uv.y;

	    vec4 color = texture2D(texture, uv);
	    float alpha = 1.0;
	    if (color.b<0.14){    discard;}
	    color.r=1.0-color.r;
            color.g=1.0-color.g;
	    color.b=1.0-color.b;

	    gl_FragColor=color;
            
            }

What we need to know in pixel shader? Time, uPositionAndSize of clone, and some varying random number (as uPositionAndSize and uvAlphaTime - from vertex shader). User can add custom uniforms to sprite: wind direction, wind strength, lights position etc... All - as now.

One thing to add to pixel shader - to identify clone by himself from shader:

varying vec4 uPositionAndSize;

and can add random too - very useful

varying float random; or varying vec4 random;

 

Gio

I think there is a misunderstanding here. We are already doing this. And it works with the default shader. 

But if you have a custom shader with your own uniforms, you are going to need different values of those uniforms, one value for each sprite for each uniform.

coal

>But if you have a custom shader with your own uniforms, you are going to need different values of those uniforms, one value for each sprite for each uniform.

I want to say that current implementation of custom shaders must not be changed. They stay as is with performance impact (usefull for rendering water for example, or for texture scrolling). But additionaly adds new implementation: class Particles with property "texture", "particle numbers", arrays of size and position.

a) Each sprite must have individual uniform values. As current.

b) Each particle (new implementation ) must NOT have individual uniform values. Uniforms are common for all 100 000 trees or grass or clouds or snow. Uniforms for particles - are screen forces (wind, gravitation and others, which are time function, so wind strength and gravity strength). Also uniforms for particles - are lights positions. Also - speed and direction of human movement if it centered. This are common uniforms for all scene. No need in individual uniforms for particles. Particles - is one element, but fragmented. Each particle must know only it's position on screen or index. May be speed (?), but it can be calculated from index, by math... May be initial speed for explosion - but this is common uniform too, for all particle system.

Implementation of particles in editor UI could be such: user drags one "New Particle" to scene, set it property "particles count" and edit each particle initial size and position - as done for layers. And write one shader for all particles. And changes positions of particles from js func.

coal

Each particle must know only it's position on screen or index

Index is necessary, I think. And all particles behavior - is shader function F(index, time) or F(screenPosX 0..1, screenPosY 0..1, time). Or F(index, time, random, custom common forces).

Bonus here is on clean GPU calcs. Incredible performance and incredible graphics screen-wide, with only one texture and one draw call.

coal

examples of Particles, which can be used in games as idea:

interactive water http://madebyevan.com/webgl-water/

autumn http://oos.moxiecode.com/js_webgl/autumn/

snow http://oos.moxiecode.com/js_webgl/snowfall/

leaves http://www.bongiovi.tw/experiments/webgl/blossom/

stars http://kluster.j38.net/

magick https://www.iamnop.com/particles/

fluid http://edankwan.com/experiments/icicle-bubbles/

fire https://ethanhjennings.github.io/webgl-fire-particles/

 

and that all anyone can apply to 2D, by fantasy

coal

One more example: here you can see 3 trees planes scrolling with different speed. Currently this is sprites with custom shaders. But I can do the same with particles with no impact, 3 times faster. http://seganelservice.ru/WebGL/Wade1

Scroll speed here - is index function in shader, where I have 3 particles in system.

This is just example, but for backs (tree layers with perspective imitation) is useful

Gio

So basically you'd like to have a particle system in Wade.

I think it's a good idea and it's already been planned for a future version, but it'll be in 4.3 so it's going to be a while.

You realize though that this won't let you do what you originally wanted to do with your tree sprites, right? For that you'd need one set of uniforms for each sprite, as I think you said you want different offsets and different parameters for each tree,

coal

>You realize though that this won't let you do what you originally wanted to do with your tree sprites, right?

I only made some tests. I don't worry about time to wait, I found that I can do all what I want. I can make effects now, can design scenes and so on... Currently I'll make effects with less objects, and later migrate to particles. 

 

 

>For that you'd need one set of uniforms for each sprite, as I think you said you want different offsets and different parameters for each tree,For that you'd need one set of uniforms for each sprite, as I think you said you want different offsets and different parameters for each tree,

This not true. I try to explain what I mean:

Here you can see new particles demo with 100000 trees. Every tree has different color and different wind animation (if you notice). And can have any different transform/effect with one shader only.

 

http://seganelservice.ru/WebGL/HundredThousandTrees2/

 

Shaders code:

<script type="x-shader/x-vertex" id="vertexshader">

            attribute float size;
            attribute vec3 customColor;

            varying vec3 vColor;
            varying vec3 vPosition;

            void main() {

            vColor = customColor;
	    vPosition = position;

            vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

            gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );

            gl_Position = projectionMatrix * mvPosition;

            }

        </script>

      

        <script type="x-shader/x-fragment" id="fragmentshader">

	    uniform float time;
            uniform vec3 color;
            uniform sampler2D texture;
            varying vec3 vColor;
            varying vec3 vPosition;

            void main() {

	    vec2 uv = gl_PointCoord;
	    
	    uv.x+=(1.0-uv.y)*sin(vPosition.x+(sqrt(uv.y)+time/3.0)/0.3)/10.0;
	    uv.x=fract(uv.x);
            uv.y=1.0-uv.y;

	    vec4 color = texture2D(texture, uv);
	    float alpha = 1.0;
	    if (color.b<0.14){    discard;}
	    color.r=1.0-color.r;
            color.g=1.0-color.g;
	    color.b=1.0-color.b;
	    
             
            color.r*=fract(vPosition.x/100.0);
            color.g*=fract(vPosition.z/100.0);
	    
	    gl_FragColor=color;

            }

        </script>

 

All trees are pure particles. Are pure clones. And all have the same uniforms:

uniform float time;
uniform vec3 color;
uniform sampler2D texture;

There is no custom individual uniform for each sprite/particle. But animation and color differs... How it done?

This is very simple math magic with vPosition (position of particle on screen. It needs to transform to 0..1, but I use absolute number)

wind differ:

uv.x+=(1.0-uv.y)*sin(vPosition.x+(sqrt(uv.y)+time/3.0)/0.3)/10.0;

color differ:

color.r*=fract(vPosition.x/100.0);
color.g*=fract(vPosition.z/100.0);

 

coal

you need only to pass position of particle from vertex shader to pixel shader. And that's all to add with both shaders.

varying vec2 vPosition;

also if you pass size and index of particle from vertex to pixel shader - this will be very useful too.

varying vec2 vSize;
varying float vIndex;

and all animation in pixel shader will be done by math of vPosition, vSize, vIndex and time.

Here you can found thousands pixelshader effect on pure math, without any texture and geometry.They only have one default sprite (plain geometry)

https://www.shadertoy.com/view/Ms2SD1

here is only pixel shader code. No attributes, no uniforms, no textures - pure math, nothing else absolutely... Math is power, but in my case I must know at minimal index/position of particle in pixel shader. Which are not uniforms, but varying

coal

Also, I can animate particles screen positions at onUpdate js. There is 100000 cycles on CPU, but only 1 geometry, 1 draw call, 1 texture, 1 shader, one common uniforms. And different clones animation/transformation/rendering. And this is very fast with 60 FPS

http://seganelservice.ru/WebGL/HundredThousandTrees3/

                    var pos = geometry.attributes.position.array;
                    for (var i = 0; i < particles; i++) {

                        UnitAI(i);

                        pos[i * 3] += units[i].deltax;
                        pos[i * 3 + 2] += units[i].deltay;
                    }

where in my code "geometry.attributes.position.array" is "Float32Array(particles * 3);"

This is only demo as example of capabilities.

coal

That techique I call "force field". There is some force on screen: wind, gravity, light, explosion and so on. Force is described in pixel shader by math. Initial particle values - are particles positions and sizes. Force parameters - are common uniforms. Time is common uniform. Sin/Cos do 90% of individual work...

And all on screen will become live. Imagine grass field on wind or clouds on sky, or rainy weather... No problem in 2D, effects are the same...

coal

Such particles effects (force fields) will allow to make 2D game like Ori (extreme beautiful, with fallen leaves and so on). I'm not sure that I can do such beauty, because I'm not professional in design. But other people will be able to do this.

coal

I want to say - editor allows to do all currently. Difference between sprites and particles is only in speed, in 100 times (and *100 objects). But I can do all with sprites now, using less sprites, not using shaders, using one texture of 1000 rain particles and moving it... Imitation not a problem now, but I show here way to speed up...

cleo

I haven't really followed the whole thread here, but it seems to me that what you want should be easy to achieve (Gio correct me if I'm wrong):

In layer.js you have a variable called vbForOneElement that is the vertex buffer data for one sprite in a batch. Could you just add one extra element there that is the sprite index in the batch? Then edit batchedVertexShaderSource to add a "varying int" that is passed to the pixel shader and contains the value of the sprite index.

coal

No, currently I can't implement particles myself in framework (If I understand correct)... Your information is very interesting and I'll play with it. But the main difference between current (sprites) and wanted (particles) is:

a) Look at scene with 1000 snow sprites, as http://seganelservice.ru/WebGL/Wade5/

You can suppose, that all is fast, but GPU load is 50%. This is load for super mega giper AAA+ game of top quality. But I have only snow and some scroll...

Why so? Because there is 1000 draw calls. Snow texture passes from CPU to GPU - 1000 times per frame. 60000 times per second. 600 000 000 Bytes per second are passed from CPU to GPU. This is bottleneck! And this is only for 10Kb texture. What if texture is 1Mb size (4000*4000pixels)?

And if I set snow with 10000 sprites - all will simply hang.

b) Scene with 1000 snow particles. Or with 1 000 000 snow particles.

GPU will stay 4-10% for any count! Because there is only 1 draw call for all particles, which can be rendered individually. But that needs patching of framework, I can't do this by any method.

P.S. with particles you can make all, that with sprites. But this will be full GPU calc, which is extreme fast. CPU calcs must be 0%, GPU calcs must be 100%. And no 1000 passings of one texture must present... This is how AAA+ games are done.

coal

I found bypass of that current restriction. I can draw all snow in one posteffect, through raymarching. But this is technique from another planet. Can you understand how that works with no textures, no attributes, no uniforms, no js, only pure math?

http://glslsandbox.com/e#53264.0

http://glslsandbox.com/e#52614.0

http://glslsandbox.com/e#52272.0

I think no... And I understand it only by 10%... It's very hard technique... Crazy and inside-out...

I'll learn it soon. But raymarching is very complex at GPU, and gives more GPU load, than particles... The same 40% GPU load, but calcs, not texture passing...

Gio

Interesting suggestion there cleo, thanks for that. I think it's spot on.

Let me clarify:

"Because there is 1000 draw calls. Snow texture passes from CPU to GPU - 1000 times per frame"

No! This is not true, I've already explained it above. The texture is sent to the GPU only once.

You have 1000 draw calls if and only if you use a custom shader (and even then, CPU to GPU transfer of textures is not done every time, just once). With the default shader, you have 1 single draw call for 1000 sprites. And you absolutely do not need a custom shader for those snow particles, it can easily be done with the default one.

Gio

Having said that, I think what we could do is add batching to custom shaders that do not use uniforms. That is relatively simple and, along with cleo's suggested modification should allow you to do most of the things you mentioned in this thread.

If we do that, even with a custom shader (if you don't use uniforms) you will still have 1 single draw call for your 1000 sprites.

coal

Yes, I mean custom shader. On demo there is custom shader on snowflake.

vec2 uv = uvAlphaTime.xy; 
vec4 color = texture2D(uDiffuseSampler, uv); 
float fAlpha = color.r/2.0; 
if (color.b<=0.1){fAlpha=0.0;}; 
gl_FragColor=color; 
gl_FragColor.w = fAlpha;

Look at 10000 snowflakes:

http://seganelservice.ru/WebGL/Wade26/

not hanging, but with breaks... But I've seen the same or more speed with 1 000 000 elephants on particles... http://seganelservice.ru/WebGL/OneMillionSprites/

That is the main difference - at 100 times... I try to say only about difference

coal

I know - I can remove shader, I can optimize smth, can make other texture. I know, speech is not about that.

coal

Having said that, I think what we could do is add batching to custom shaders that do not use uniforms. That is relatively simple and, along with cleo's suggested modification should allow you to do most of the things you mentioned in this thread.

If we do that, even with a custom shader (if you don't use uniforms) you will still have 1 single draw call for your 1000 sprites.

 

Thank's a lot! I don't speed up you, just discuss to make framework better. Thanks!

coal

And the last suggestion: if you could add to framework display of FPS and draw calls, as done in my demos - it would be great!

I used for that: stats.min.js (internet)

<script src="../js/libs/stats.min.js"></script>

stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild(stats.domElement);

and threex.renderstats.js
 

<script src="../js/threex.renderstats.js"></script>

var rendererStats = new THREEx.RendererStats();

rendererStats.domElement.style.position = 'absolute'
rendererStats.domElement.style.left = '0px'
rendererStats.domElement.style.bottom = '0px'
document.body.appendChild(rendererStats.domElement)

than onUpdate()

stats.update();

rendererStats.update(renderer);

That info is needed not only in debug, but in runtime too (may be some framework flag), because speed differs.

coal

or may be I can add them myself... But I don't know where, my js is so poor as english...

krumza

Here the snippet when i need to know fps:

App = function(){
  //define var as you wish
  var FPSmeter = 0;	    
  var fps = document.getElementById('fps');


  //inside init()
  this.init = function()    {
    update();
    setInterval(update, 1000);
    wade.setMainLoop(FpsonUpdate);

    var FpsonUpdate = function(){
	  FPSmeter++;      	
    };	   

    var update = function() {        
	  fps.innerHTML = 'FPS: ' + FPSmeter;
	  FPSmeter = 0
    };
  }
}

And dont forget  add 

<div id="topbar_fps">FPS: 60</div>

on the index.html

***

very interesting theme 

and if you interesting - here a test for particles in wade http://kardan2.smartinvestclub.ru/ 

~40k sprites whitout lose fps 

 

 

coal

Thanks a lot! Very much! )))

coal

But... With 40K... I have 20FPS on your demo. And brakes... This are not particles, unfortunetly. This are sprites with default shader, I guess. I've tested them already. Only 10K, maybe 20K, may be 40K. But not one million, which has the same speed as shown, when implemented as particles. Also, if you make your demo full screen - it will be still lower, I guess... 

But, anyway, I invented how to render very realistic snow in one wade post process shader without any texture and any sprite (1 sprite only for layer adding). I"ve implemented this with raymarching. I'll show that demo soon - it's pretty, but have no time to finish. Shader can be used for rain too, and, I think, in many other usages. But it is procedural (GPU intensive), and slower, than particles, even whith js cycle of all particles position change.

P.S. ru domain - it's funny ))))

coal

I can't get in how to show FPS in game... Very stupid ))) 

This is simply null, not found (I've added to index.html)

var fps = document.getElementById('fps');

But I've done raymarching snow in posteffect shader. No textures, no sprites, no particles. Any speed, any density, much layers, and any other... Ready at 90%, but needs some tuning. Can be used for rain and anything else with little modification.

http://seganelservice.ru/WebGL/Wade28/

I guess FPS here is about 60.

There are much parameters in shader. Only one uniform: float kScreen (screen width/screen height, set onResize from js)

float time = uvAlphaTime.w;
vec2 originaluv=uvAlphaTime.xy;// (gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y); 
vec3 finalColor=vec3(0.0);

//back highlight
float c=0.0;//smoothstep(1.,0.3,clamp(originaluv.y*.3+.8,0.,.75));

//wind speed/direction
float wind=25.1;
//screen resolution koef
//const float kScreen = 2.;
//count of snow layers
const int layersCount = 10;

//radius of snowflake
float snowRadius = .1;
bool mix = true;

//начальный масштаб (top слоя)
float startScale = 5.;
//scale decrement for layers(i)
float scaleDecrement = 1.1;

//snow falling speed
float fallingSpeed=5.1;
//snow layer falling decrement
float fallingSpeedDecrement=0.5;
//начальный bottom, до которого падать
float startBottom=-0.;//0.5;

//common snow density
float density=.1;
//snow density layer increment
float densityIncrement=1.5;

//snow opacity
float snowOpacity=1.;
//snow opacity layer decrement
float snowOpacityDecrement=1.1;

//snow start glow
float glowRadius = 0.04;
//snow glow layer decrement
float snowGlowDecrement=1.;

//snow color
vec3 snowColor = vec3(1.0,1.,1.);

//snow color layer decrement
//TODO
//float snowColorDecrement = 2.;



//scale of top layer snow
float scale=startScale;

//самый крупный план - самый быстрый
for(int i=0;i<layersCount;++i)
{
    //восстанавливаем оригинальную координату текстуры
    vec2 uv=originaluv;
    //screen resolution koef
    uv.x*=kScreen;
    //add wind
    uv.x+=wind/float(i)*time/100.0;
    //uv.x=fract(uv.x);
    
    //bottom Y in current layer(i), z proection
    //float bottom = float(i)/float(layersCount)+startBottom;
    float bottom = 1.0-5.0/float(i)+startBottom;
    
    //decrement scale for layer, z proection
    scale*=scaleDecrement;
    
    //falling speed in layer(i), z proection
    //float layerFallingSpeed=fallingSpeed*1./float(i+1);
    fallingSpeed*=fallingSpeedDecrement;

    float snow;
    //restrict bottom y
    if(uv.y<bottom)
    {
        snow = 0.;
    }
    else
    {
        //чем ближе - тем больше размер и тем больше скорость падения!
        uv.y+=time*(1./scale+fallingSpeed);
        //ветер по x
        //uv.x+=(time/1.0+sin(uv.y+time)/2.0)/scale;
        //uv.x+=sin(uv.y+time*0.5)/scale;
        //uv.x+=uv.x+=(time/100.0+sin(uv.y+time/1.0)/2.0)/scale*10.0;
        
        //плотность на экране
        density*=densityIncrement;
        uv*=scale*density;
        vec2 s=floor(uv),f=fract(uv),p;
        float k=3.,d;
        p=.5+.35*sin(11.*fract(sin((s+p+scale)*mat2(7.,5.,16.,3.))*5.))-f;
        d=length(p);
        //inner snow
        if (d<snowRadius)
        {
            k=min(d,k);
            k=smoothstep(0.,k,.01);//sin(f.x+f.y)*glowRadius
            snow = k;
        }
        else
        {
            //outer snow glow - TODO!
            //k=min(d,k);
            //k=smoothstep(0.,k,sin(f.x+f.y)*glowRadius*d);
            //snow = k;
            //outer glow
        }
    }
    snowOpacity/=snowOpacityDecrement;
    c+=snow*snowOpacity;
    glowRadius/=snowGlowDecrement;
    
    finalColor=(snowColor*c);
}

vec4 realColor = texture2D(uDiffuseSampler, uvAlphaTime.xy);

if (mix)
{
    gl_FragColor = vec4(finalColor,1.0)+realColor;
}
else
{
    if (finalColor.r>0.)
    {
        gl_FragColor = vec4(finalColor,1.0);
    }
    else
    {
        gl_FragColor = realColor;
    }
}
//gl_FragColor.r=max(gl_FragColor.r,realColor.r);
//gl_FragColor.g=max(gl_FragColor.g,realColor.g);
//gl_FragColor.b=max(gl_FragColor.b,realColor.b);

 

coal
Only one uniform: float kScreen (screen width/screen height, set onResize from js)

Gio! If you can add that koef as predefined in shaders, it will be very useful. This is needed in any raymarching shader effect. 

If you add that koef in any shader, as you've done with uvAlphaTime - it will be great! This is strongly needed information in procedural effects with no texture

coal

raymarching snow with max color blending (not adding color)

http://seganelservice.ru/WebGL/Wade29/

krumza

You not full copy my snippet! You forgot add update function)

But if you use an editor  - it cant work because editor left only main div

try  another - create an object with text sprite and update it/

 

how it work :

we create 2 functions

var FpsonUpdate = function(){
	  FPSmeter++;      	
    };	   

It simple increment whitch we stick to wade main update in

wade.setMainLoop(FpsonUpdate);

That mean when wade global update canvas we launch our increment function

Then second important part - update function

var update = function() {        
        	  //some action with FPSmeter;
        	  FPSmeter = 0
            }; 

We launch this function with timeout that mean some action with FPSmeter variable and turn in to zero again

In yor case you can create some object, than add textsprite to it and change innerHTML method to textSprite.setText()

in your case it be:

       // load a scene
		wade.loadScene('your_scene.wsc', true, function()
        {         
            
            // the scene has been loaded, do something here 
            
            var FPSmeter = 0
            
            var FpsonUpdate = function(){
        	    FPSmeter++;      	
            };	
            
            var update = function() {
              //add scene object and give name it and look index of textsprite
        	  wade.getSceneObject('a').getSprite(0).setText('FPS: ' + FPSmeter);
        	  FPSmeter = 0
            };  
            
            wade.setInterval(update, 1000);
            
            wade.setMainLoop(FpsonUpdate);


        });

 

coal

That not works in wade, I tried to copy snippet full,

App = function()
{
  //define var as you wish
  var FPSmeter = 0;	    
  var fps = document.getElementById('fps');


  //inside init()
  this.init = function()    {
    update();
    setInterval(update, 1000);
    wade.setMainLoop(FpsonUpdate);

    var FpsonUpdate = function(){
	  FPSmeter++;      	
    };	   

   
  }
  
   var update = function() {        
	  fps.innerHTML = 'FPS: ' + FPSmeter;
	  FPSmeter = 0
    };
	
    this.onResize=function()
    {
        var k = wade.getScreenWidth()/wade.getScreenHeight();
        wade.setLayerCustomProperty(1,"kScreen",k);
    }
	

};

here is result

This is because  fps is null. I don't know why... I added to index.html such code:

<div id="fps">FPS: 60</div>

<div id="container" style="border:0; width:800px; height:600px;">
    <div id="wade_main_div" width="800" height="600" tabindex="1">
    </div>
    
</div>
<div id="fps">FPS: 60</div>

but it's not found from var fps = document.getElementById('fps'); 

krumza

This is because wade editor clean html and left only #container

maybe it feature but if you write in editor

<div id="container" style="border:0; width:800px; height:600px;">
    <div id="wade_main_div" width="800" height="600" tabindex="1">
    </div>
    
</div>
<div id="fps">FPS: 60</div>

 

editor left only 

<div id="container" style="border:0; width:800px; height:600px;">
    <div id="wade_main_div" width="800" height="600" tabindex="1">
    </div>
    
</div>

in iframe

Use solution on bottom of my previous post

coal
.setText('FPS: ' + FPSmeter);

This is the way! Thank you!

coal

I have success, and watch fps on TextSprite, but...

It shows 60FPS, when I set 3000 snow layers in shader and the real watched fps by eyes is 1 fps... Unfortunately that not works correctly in wade main loop (or with postprocessing shaders). Why - is mystic... In other cases may be it works...

coal

I think this is so, because main loop is not render loop... So. correct FPS can make only Gio

krumza

this show CPU load not GPU, 

i think you must use chrome rendering tools in console  in

console - more tool - rendering 

coal

But it is possible to show real FPS in wade. In three.js it is made with stats.js https://github.com/mrdoob/stats.js/

 

It shows real FPS (viewable by user on screen) in onRender cycle. This cycle works not 60 times per sec, but right away from previous cycle (60fps is maximum for webgl. But if there is brakes - it will update slower)

function animate() {

                    requestAnimationFrame(animate);                   
                    render();
                    stats.update();

                }

 

krumza

You can use stats.js too!

Bookmarklet
You can add this code to any page using the following bookmarklet:

Stats js will be works with wade 

coal

I tried, but with no result )))) May be my fault, or may be wade restrictions... I simply don't see it. Also, I can't add stats.update() to render cycle...

krumza

just open console and paste bookmarklet code

 

I have a question - why you use a 

var k = wade.getScreenWidth()/wade.getScreenHeight();
	    wade.setLayerCustomProperty(1,"kScreen",k);

If you change wade layer mode to 'container' - there is no need in this property

 

Next question - if you make an platformer game - why your snow stick to layer

In real game player move, and all background move to him, i.e. you must stick snow to player object, and move layer with snow with player speed

coal

>If you change wade layer mode to 'container' - there is no need in this property

I didn't know, thanks!

>Next question - if you make an platformer game - why your snow stick to layer? In real game player move, and all background move to him, i.e. you must stick snow to player object, and move layer with snow with player speed

Yes, this will be new shader uniform "scroll speed", wich just will be added inside shader to wind. But there is more difficult thing - you must stick snow posteffect to different layers with different params, if you want real depth-imitation. I'll show such demo, but later. It will be real true render, but firstly my friend-designer have to draw layers content.

coal

Stats.js works with bookmarklet code in console. But don't work from app.js. May be my fault, don't know... Such simple things, but very hard for me )) If it were integrated statistic in wade...

coal

Gio,

I fill currently my own shaders library and think...

Why not allow to select custom user shader from list? Maybe files with extension *.glsl? It would be very conviniently.

And more convinient - if custom shader uniforms will be loaded too...

Gio

Just to be sure I understand, do you mean that we should look for files with a glsl extension in the project, parse them and add them to the list?

If that's what you mean, I think it's a great idea. Certainly worth doing.

coal

Yes, thanks!