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?

Comments 16 to 30 (71 total)
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.

Post a reply
Add Attachment
Submit Reply
Login to Reply