Вращение кривой в шейдерном фрагменте GLSL - PullRequest
0 голосов
/ 16 мая 2018

Я пытаюсь найти правильную математику для поворота и перевода кривой, отображаемой в фрагментном шейдере.

Что я пытаюсь сделать, это определить кривую, например, синусоидальную, влокальная система координат, поверните ее и переведите.Примерно так:

enter image description here

Это было сделано в MATLAB со следующим кодом:

dens = 1080;
x = linspace(-1.0, 1.0, dens);        
y = 0.1*sin(25.0*x);

imax = 25;
for i = 1:imax    

    %transformation matrix:
    ang = (i/imax)*0.5*3.14;
    c = cos(ang); s = sin(ang);          
    T = [c,-s;s,c];

    %translation:
    P = [0.5;0.5];

    %transformed coordinates:
    xt = T(1,:)*[x;y] + P(1);
    yt = T(2,:)*[x;y] + P(2);

    plot(xt,yt);
    xlim([0 1.0]); ylim([0 1.0]); drawnow;
end

Для теста GLSL I 'm с помощью Book of Shaders Editor со следующим кодом (также можно посмотреть в интерактивном режиме здесь ):

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

uniform float u_time;
uniform vec2 u_resolution;

// Plot a line on Y using a value between 0.0-1.0
float plot(vec2 st, float pct){
  return  smoothstep( pct-0.02, pct, st.y) -
          smoothstep( pct, pct+0.02, st.y);
}

float plotTransformed(vec2 st, float pct, vec2 transl, float ang){

    float c = cos(ang); float s = sin(ang);    
    mat2 trans = mat2(c,-s,s,c);    
    st = trans * st;

    st -= transl;

    return  smoothstep( pct-0.02, pct, st.y) -
          smoothstep( pct, pct+0.02, st.y);
}

void main(void) {
    bool use_plot_function = true;

    float mx =  max(u_resolution.x, u_resolution.y);
    vec2 uv = gl_FragCoord.xy /mx;
    vec3 color = vec3(0.4,0.4,0.4);

    //some screen position:
    vec2 p = vec2(0.5, 0.5);

    //the curve:
    vec2 cp = vec2(
        uv.x,
        0.08*sin(uv.x*40.0)
    );

    //the angle to rotate:
    float ang = -0.4 * 3.14 * sin(u_time);

    //Transform coordinates:
    float c = cos(ang); float s = sin(ang);    
    mat2 trans = mat2(c,-s,s,c);    
    vec2 cp_t = trans * cp;    
    cp_t +=p;



    if(use_plot_function){
        //Attempt 1: plot unrotated original curve translated upwards: 
        float curve1 = plot(uv, cp.y + p.y);
        color.g *= curve1;    

        //Attemp 2: plot the transformed curve using plotTransformed, rotates first, then translates:
        float curve2 = plotTransformed(uv, cp.y, p, ang);
        color.r *= curve2;

        //Attempt 3: curve is transformed first then ploted:
        float curve3 = plot(uv, cp_t.y);
        color.b *= curve3;
    }            
    else{
        float plotThk = 0.02;

         //Attempt 1: change color based on distance from unrotated original curve: 
        float dist = distance(uv, cp + vec2(0.0, p.y));
        if(dist < plotThk)
            color.g *= (1.0 -dist)/plotThk;   

        //Attempt 2: change color based on distance from transformed coordinates:
        dist = distance(uv, cp_t);
        if(dist < plotThk)
            color.r *= (1.0 -dist)/plotThk;   

    }

    gl_FragColor = vec4(color,1.0);
}

В приведенном выше коде есть два режимакоторый можно переключать с use_plot_function, установленным на false или true.

Первый режим пытается построить график с использованием функций plot() & plotTransformed().Второй режим устанавливает цвет фрагмента на основе расстояния от вычисленных координат кривой.

Результат первого режима с use_plot_function, установленным на true:

Three different attempts with use_plot_function set to true

Результат второго режима с use_plot_function, установленным на false:

use_plot_function set to false

Очевидно, я не понимаю, какэто должно быть сделано в фрагментном шейдере.

Как правильно определить преобразованную кривую в шейдерном фрагменте GLSL?

1 Ответ

0 голосов
/ 16 мая 2018

Слишком ленив, чтобы просмотреть ваш код, он выглядит слишком сложным для простого sinwave, но вращение в вершинном шейдере намного проще, однако, если вы настаиваете на фрагментном шейдере, я бы:

  1. определить повернутую систему координат по базисным векторам

    uniform float a;               // rotation angle [rad]
    vec2 U = vec2(cos(a),sin(a)); // U basis vector (new x axis)
    vec2 V = vec2(-U.y,+U.x);     // V basis vector (new y axis)
    vec2 O = vec2(0.5,0.5);       // O origin (center of rotation in global coordinates)
    

    , это позволит вам вычислить повернутую позицию любого фрагмента ... так что если ваша позиция без поворота в диапазоне <-1,+1> равна:

    uniform vec2 pos;
    

    тогда повернутое положение для нашего pos будет:

    float u=dot(pos-O,U);
    float v=dot(pos-O,V);
    

    , и вы даже можете конвертировать обратно из u, v в x, y, если вам нужно:

    pos=O + u*U +v*V;
    
  2. параметр

    для любой кривой, которую мы обычно используем параметр.В вашем случае это угол синусоиды, который также является икс-координатой повернутых координат (которая равна some_constant0 + u*some_constant1).

  3. параметрическая кривая во фрагменте

    , поэтому, когда у нас есть параметр, мы просто вычисляем y точки кривой, вычисляем расстояние до нее положения нашего фрагмента и, если расстояние больше половины толщины кривой, отбрасываем фрагмент ...

    const float half_thickness=0.02;
    vec2 dP;
    float u,v;
    u=dot(pos-O,U);
    v=0.08*sin(u*40.0);
    dP=O + u*U +v*V - pos;
    if (length(dP)>half_thickness) discard;
    // here output your color
    

Это все, что вы просто визуализируете одним QUAD, покрывающим ваш экран (или кривую) и угол поворота.Вот моя попытка (собрать все вместе) ...

Вершина:

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
layout(location=0) in vec2 in_pos;
out smooth vec2 pos;
//------------------------------------------------------------------
void main(void)
    {
    pos=in_pos;
    gl_Position=vec4(in_pos,0.0,1.0);
    }
//------------------------------------------------------------------

Фрагмент:

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec2      pos;
out layout(location=0) vec4 col;
//uniform float a;                  // rotation angle [rad]
const float a=0.3;                  // rotation angle [rad]
const float half_thickness=0.02;    // curve half thicess
//---------------------------------------------------------------------------
void main(void)
    {
    vec2 U = vec2(cos(a),sin(a)); // U basis vector (new x axis)
    vec2 V = vec2(-U.y,+U.x);     // V basis vector (new y axis)
    vec2 O = vec2(0.5,0.5);       // O origin (center of rotation in global coordinates)in smooth vec3      pos;    // ray start position

    vec2 dP;
    float u,v;

    u=dot(pos-O,U);
    v=0.08*sin(u*40.0);
    dP=O + u*U +v*V - pos;
    if (length(dP)>half_thickness) discard;

    col=vec4(0.2,0.3,0.5,1.0);
    }
//---------------------------------------------------------------------------

preview

Как видите, я использовал жестко закодированный цвет и угол.Вы можете изменить угол с равномерным и цветным с равномерным или использовать прямой цвет от VBO / VAO или glColor ...

Я использовал O(0.5,0.5), если вы хотите вращаться вокругв центре экрана, затем используйте O(0.0,0.0) вместо ...

...