如何在着色器中有效地绘制直线和圆圈

How to draw lines and circles in a shader efficently

我已经使用这个网站创建了一个显示雪人和一些雪花的着色器:
http://glslsandbox.com/e#54840.8
如果 link 不起作用,代码如下:

#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;



}

它的工作方式是,对于屏幕上的每个像素,着色器检查每个元素(按钮、body、头部、眼睛、嘴巴、胡萝卜、雪花)是否在一个区域内,n在这种情况下,它会用当前绘制颜色替换该位置的当前颜色。

所以我们有 O(pixels_width * pixels_height * elements) 的复杂度,这会导致当太多雪花在自己的屏幕上时着色器变慢。 所以现在我想知道,如何优化这段代码?我已经考虑过使用边界框甚至 3d 八叉树(我猜那将是四叉树)来快速丢弃某个像素(或片段)区域之外的元素。

还有人知道如何优化此着色器代码吗?请记住,每个着色器执行都完全独立于所有其他着色器,我不能使用任何总体结构。

您需要将屏幕分成多个区域,"tiles" 并计算每个图块的雪花。瓷砖将具有相同数量的雪花并共享相同的种子,因此离开瓷砖边界的一个粒子将有一个相同的粒子进入下一个瓷砖,使其看起来无缝。根据您的设置,图案可能仍会出现,但您可以考虑添加额外的统一转换,可能基于最终屏幕位置。

附带说明一下,通过删除所有条件分支(并查看过程中的 anti-aliased),您绘制圆圈的方法可能会更有效,并且可以删除 length() 生成的平方根。