【问题标题】:Curve rotation in GLSL fragment shaderGLSL 片段着色器中的曲线旋转
【发布时间】:2018-05-16 16:21:38
【问题描述】:

我正在尝试找出正确的数学运算来旋转和平移片段着色器中显示的曲线。

我试图完成的是在局部坐标系中定义一条曲线,例如正弦曲线,旋转它然后平移它。像这样的:

这是在 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 测试,我使用带有以下代码的 Book of Shaders Editor(也可以以交互方式查看 here):

#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 设置为falsetrue

第一种模式尝试使用函数plot() 和plotTransformed() 进行绘图。 第二种模式根据与计算出的曲线坐标的距离为片段设置颜色。

use_plot_function 设置为 true 的第一种模式的结果:

use_plot_function 设置为false 的第二种模式的结果:

显然我误解了如何在片段着色器中完成此操作。

我应该如何在 GLSL 片段着色器中正确定义转换曲线?

【问题讨论】:

    标签: glsl transformation fragment-shader


    【解决方案1】:

    懒得看你的代码对于简单的正弦波来说看起来太复杂了,但是在顶点着色器中旋转要简单得多但是如果你坚持使用片段着色器,我会:

    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)
      

      这将使您能够计算任何片段的旋转位置......所以如果您的片段在&lt;-1,+1&gt; 范围内的未旋转位置是:

      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. 参数

      对于我们通常使用参数的任何曲线。在您的情况下,它是正弦波 wjich 的角度,也是旋转坐标的 x 坐标(等于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);
        }
    //---------------------------------------------------------------------------
    

    如您所见,我使用了硬编码的颜色和角度。您可以使用统一更改角度和使用统一颜色或使用 VBO/VAOglColor ...

    的直接颜色

    我使用O(0.5,0.5),如果你想围绕屏幕中心旋转,然后使用O(0.0,0.0)...

    【讨论】:

    • 不错。我打算在没有顶点着色器的情况下这样做。这是基于您的实际答案的代码:thebookofshaders.com/edit.php?log=180516082237 我仍然需要再阅读代码几次才能更好地理解您在那里所做的转换,但这很好,谢谢。
    • @remi 如果您使用一阶推导计算与曲线(法线)垂直方向的厚度,则可以进一步改进。因此,从一阶导数构造法线,而不仅仅是对法线和 dP 进行点积......结果是用于比较的标量距离,这将导致曲线的恒定厚度,而不管斜率如何
    • @remi 转换与使用 3x3 2D 统一矩阵相同,我刚刚剖析了它。为了更好地理解,请参阅Understanding 4x4 homogenous transform matrices,因此您可以改用 3x3 矩阵,但我认为直接使用向量更具教学意义
    • @remi 和 U,V 一起形成 2x2 旋转矩阵
    • @remi (x,y) = O + u*U +v*V(x,y,w)=M * (u,v,1) 相同,其中M 是由U,V,O 列构成的3x3 矩阵,最后一行是(0.0,0.0,1.0) ...反过来由(u,v,w) = Inverse(M)*(x,y,1) .... 2x2 矩阵在向量中没有 O 最后一行和 1,w,但仅围绕 (0.0,0.0) 旋转
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-12
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    • 2020-12-05
    • 2018-12-25
    相关资源
    最近更新 更多