Как эффективно рисовать линии и круги в шейдере - PullRequest
1 голос
/ 22 мая 2019

Я использовал этот сайт для создания шейдера, который отображает снеговика и несколько снежинок:
http://glslsandbox.com/e#54840.8
Если ссылка не работает, вот код:

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



}

Способ работает так, что для каждого пикселя на экране шейдер проверяет каждый элемент (кнопку, тело, голову, глаза, рот, морковь, снежинку), находится ли он внутри области, и в этом случае он заменяет текущий цвет в этой позиции с текущим цветом рисования.

Таким образом, мы имеем сложность O(pixels_width * pixels_height * elements), которая приводит к замедлению шейдера, когда слишком много снежинок имеют собственный экран. Так что теперь мне было интересно, как этот код может быть оптимизирован? Я уже думал об использовании ограничивающих рамок или даже 3d Octree (я думаю, это будет quadtree), чтобы быстро отбрасывать элементы, которые находятся за пределами определенной области пикселей (или фрагментов).

У кого-нибудь есть другая идея, как оптимизировать этот шейдерный код? Помня, что каждое выполнение шейдера совершенно не зависит от всех остальных, и я не могу использовать какую-либо всеобъемлющую структуру.

1 Ответ

0 голосов
/ 27 мая 2019

Вам нужно будет разбить ваш экран на регионы, «плитки» и вычислить снежинки на плитку. Плитки будут иметь одинаковое количество снежинок и иметь одинаковое начальное число, так что одна частица, покидающая границу плитки, будет иметь одинаковую частицу, попадающую в следующую плитку, что сделает ее бесшовной. Шаблон все еще может отображаться в зависимости от ваших настроек, но вы можете рассмотреть возможность добавления дополнительного равномерного преобразования, возможно, на основе конечной позиции экрана.

Кстати, ваш метод рисования кругов может быть более эффективным, если удалить все условные ветвления (и выглядеть сглаженными в процессе) и избавиться от квадратного корня, сгенерированного функцией length ().

...