1

I have used this website to create a shader that displays a snowman and some snowflakes:
http://glslsandbox.com/e#54840.8
In case the link doesn't work, heres the code:

#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
uniform sampler2D backbuffer;

#define PI 3.14159265

vec2 p;
float bt;


float seed=0.1;
float rand(){
    seed+=fract(sin(seed)*seed*1000.0)+.123;

    return mod(seed,1.0);
}


//No I don't know why he loks so creepy

float thicc=.003;
vec3 color=vec3(1.);
vec3 border=vec3(.4);



void diff(float p){
    if( (p)<thicc)
        gl_FragColor.rgb=color;
}
void line(vec2 a, vec2 b){

    vec2 q=p-a;

    vec2 r=normalize(b-a);

    if(dot(r,q)<0.){
        diff(length(q));
        return;
    }

    if(dot(r,q)>length(b-a)){
        diff(length(p-b));
        return;
    }

    vec2 rr=vec2(r.y,-r.x);




    diff(abs(dot(rr,q)));


}
void circle(vec2 m,float r){
    vec2 q=p-m;
    vec3 c=color;
    diff(length(q)-r);
    color=border;
    diff(abs(length(q)-r));
    color=c;
}

void main() {
    p=gl_FragCoord.xy/resolution.y;

    bt=mod(time,4.*PI);
    gl_FragColor.rgb=vec3(0.);

    vec2 last;


    //Body
    circle(vec2(1.,.250),.230); 
    circle(vec2(1.,.520),.180);
    circle(vec2(1.,.75),.13);

    //Nose
    color=vec3(1.,.4,.0);
    line(vec2(1,.720),vec2(1.020,.740));        
    line(vec2(1,.720),vec2(.980,.740));     
    line(vec2(1,.720),vec2(.980,.740));     
    line(vec2(1.020,.740),vec2(.980,.740));     


    border=vec3(0);
    color=vec3(1);
    thicc=.006;
    //Eyes

    circle(vec2(.930,.800),.014);
    circle(vec2(1.060,.800),.014);

    color=vec3(.0);
    thicc=0.;

    //mouth
    for(float x=0.;x<.1300;x+=.010)
        circle(vec2(.930+x,.680+cos(x*40.0+.5)*.014),.005); 


    //buttons
    for(float x=0.02;x<.450;x+=.070)
        circle(vec2(1.000,.150+x),0.01);    


    color=vec3(0.9);
    thicc=0.;

    //snowflakes
    for(int i=0;i<99;i++){
         circle(vec2(rand()*2.0,mod(rand()-time,1.0)),0.01);
    }

    gl_FragColor.a=1.0;



}

The way it works is, that for each pixel on the screen, the shader checks for each elment (button, body, head, eyes mouth, carrot, snowflake) wheter it's inside an area, n which case it replaces the current color at that position with the current draw color.

So we have a complexity of O(pixels_width * pixels_height * elements), which leads to to the shader slowing down when too many snowflakes are own screen. So now I was wondering, how can this code be optimized? I already thought about using bounding boxes or even a 3d Octree (I guess that would be a quadtree) to quickly discard elements that are outside a certain pixel (or fragments) area.

Does anyone have another idea how to optimize this shadercode? Keeping in mind that every shader execution is completely independant of all others and I can't use any overarching structure.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
user2741831
  • 2,120
  • 2
  • 22
  • 43
  • 1
    Create simple geometry for the shapes and use textures? – Michael Dorgan May 22 '19 at 21:15
  • The site has no texture support And I wabt it scalable anyway – user2741831 May 22 '19 at 21:20
  • 1
    Circle inclusion is usually done with radius squared checks instead of length checks (which often require a sqrt and other fun). But honestly, without the system being able to report to you where it is spending its time, you are guessing at where the slowdowns actually are. – Michael Dorgan May 22 '19 at 22:03
  • You can optimize it by doing things the normal way with triangles and polygons instead of using a single GLSL shader. The only point of doing things in a single GLSL shader is as a puzzle, "How can I draw things in one shader". That is not how performance is achieved. Performance is achieved using traditional techniques that most high performance apps use (ie, games) by drawing with many shaders and using polygons. – gman May 23 '19 at 04:40
  • Otherwise depending on the gpu you might be able to save some time by adding hierarchical checks. Effectively `if (inGroupABCD) { groupABCD(...) } else { groupEFGH(...) } groupABCD(...) { if (inGroupAB) { groupAB() } else { groupCD() } groupAB(..) { if (inA) { doA(..) } else { doB(..) }` etc... – gman May 23 '19 at 05:37
  • The problem with the traditional approach is it isnt scalable. You cant do round things with triangles – user2741831 May 23 '19 at 07:46
  • It's the opposite. The traditional approach is scalable. the GLSL approach is not. How do you think titles like GTA5, Red Dead Redemption, or Spiderman are able to draw such large detailed worlds. Because they use the traditional approaches. You can do plenty of round things with triangles. Generally it's called using textures. All the letters this webpage are curvy and being drawing with triangles with textures mapped on them. And if you want infinite resolution there are techniques for that to but at a basic level they still use the traditional approaches. – gman May 23 '19 at 08:53
  • [here's one](https://www.bloomberg.com/news/articles/2018-08-22/super-mario-creator-warns-gaming-industry-don-t-be-too-greedy). [Here's another](http://jcgt.org/published/0006/02/02/) – gman May 23 '19 at 09:01
  • What I tried to do I develop a way to render svg graphics directly like textures onto a surface. But I guess that isn't possible – user2741831 May 23 '19 at 10:51
  • Also @gman Im not sure if webpage rendering is done on the gpu. At least on ff I m pretty sure its cpu rendered – user2741831 May 23 '19 at 10:52
  • @user2741831 SVG render in shader is possible, also curves and shapes are possible but that does not mean you do not have a geometry as input see [Draw Quadratic Curve on GPU](https://stackoverflow.com/a/31423105/2521214) ... also WEBGL and efficient rendering sounds like oxymoron or it is just me? – Spektre May 23 '19 at 11:48

1 Answers1

-1

You would need to break up your screen into regions, "tiles" and compute the snowflakes per tile. Tiles would have the same number of snowflakes and share the same seed, so that one particle leaving the tile's boundary would have an identical particle entering the next tile, making it look seamless. The pattern might still appear depending on your settings, but you could consider adding an extra uniform transformation, potentially based on the final screen position.

On a side note, your method for drawing circles could be more efficient by removing all conditional branching (and look anti-aliased in the process) and could get rid of the square root generated by length().

Brice V.
  • 861
  • 4
  • 7
  • thanks, something like this is what I eventually did, I just couldn't figure out how to move the snowflakes :http://glslsandbox.com/e#54872.2. – user2741831 May 28 '19 at 12:59