如何在着色器中制作波浪变形效果?

How to make a wave warp effect in shader?

我想制作这样的波浪变形效果:

但是我只能创建正常的正弦波。

这是我的片段着色器:

precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D s_baseMap;

vec2 SineWave( vec2 p ){
    float pi = 3.14159;
    float A = 0.15;
    float w = 10.0 * pi;
    float t = 30.0*pi/180.0;
    float y = sin( w*p.x + t) * A; 
    return vec2(p.x, p.y+y); 
}
void main(){
    vec2 p = v_texCoord; 
    vec2 uv = SineWave( p ); 
    vec4 tcolor = texture2D(s_baseMap, uv); 
    gl_FragColor = tcolor; 
}

结果是:

所以问题是如何在特定方向上扭曲波浪?

谢谢。

这是原始纹理:


更新: 我在计算y时扭曲了x轴,但结果似乎不对。

float x = p.x + p.y*tan( -0.5); 
float y = sin( w*x + t) * A;
return vec2(p.x, p.y+y); 

好的,我尝试重新创建您的效果,所以我将其用作纹理:

我拍摄了你的图片并将其调整为 512x512 所以它是 2 的幂,用黑色填充边框。由于您不共享顶点着色器,因此我创建了自己的顶点着色器。 GL 正在渲染单个四边形 <-1,+1>,没有纹理坐标或仅矩阵 glVertex2f(),单个 2D 纹理绑定到单元 0。我稍微重写了您的片段以匹配输出。我还添加了 tx,ty uniforms 以轻松地使用鼠标位置设置动画效果 <0,1> 这是着色器的第一个顶点:

// Vertex
varying vec2 v_texCoord;
void main()
    {
    v_texCoord=gl_Vertex.xy;
    gl_Position=gl_Vertex;
    }

然后是片段:

// Fragment
varying vec2 v_texCoord;        // holds the Vertex position <-1,+1> !!!
uniform sampler2D s_baseMap;    // used texture unit
uniform float tx,ty;            // x,y waves phase

vec2 SineWave( vec2 p )
    {
    // convert Vertex position <-1,+1> to texture coordinate <0,1> and some shrinking so the effect dont overlap screen
    p.x=( 0.55*p.x)+0.5;
    p.y=(-0.55*p.y)+0.5;
    // wave distortion
    float x = sin( 25.0*p.y + 30.0*p.x + 6.28*tx) * 0.05;
    float y = sin( 25.0*p.y + 30.0*p.x + 6.28*ty) * 0.05;
    return vec2(p.x+x, p.y+y);
    }

void main()
    {
    gl_FragColor = texture2D(s_baseMap,SineWave(v_texCoord));
    }

这是 tx=0.3477,ty=0.7812 的输出,在视觉上或多或少与您的示例相符:

如您所见,我在正弦波中添加了一些项,因此它也出现了偏斜失真。

如果 v_texCoord 已经在 <0,1> 范围内,则忽略

    p.x=( 0.55*p.x)+0.5;
    p.y=(-0.55*p.y)+0.5;

或将其重写为(以便收缩和系数保持原样)

    p.x=(1.1*p.x)-0.05;
    p.y=(1.1*p.y)-0.05;

如果你使用不同的纹理(不是我的)那么你需要重新调整所有系数。

[edit1] 系数含义

首先我从你的开始:

float x = sin( 10.0*p.y) * 0.15;
float y = sin( 10.0*p.x) * 0.15;

0.15是波幅,好像太大了所以我把它调低到0.05。然后10.0是频率,数字越大沿轴的波越多。通过纯粹的试验和错误,我确定它们应该是 30.0 对于 y 轴和 25.0 对于 x 轴所以波的数量匹配你想要的输出。

float x = sin( 25.0*p.y) * 0.05;
float y = sin( 30.0*p.x) * 0.05;

在此之后我发现波浪应该有点倾斜,所以在一些调整发现这个等式后我也添加了对另一个轴的依赖:

float x = sin( 25.0*p.y + 30.0*p.x) * 0.05;
float y = sin( 25.0*p.y + 30.0*p.x) * 0.05;

两个轴之间的系数相同(很奇怪但工作我期望我需要在轴之间有不同的系数)。之后这只是为每个轴找到正确相位的问题所以我添加了由鼠标位置控制的相移 (tx,ty) <0.0,1.0> 所以我得到了最终的:

float x = sin( 25.0*p.y + 30.0*p.x + 6.28*tx) * 0.05;
float y = sin( 25.0*p.y + 30.0*p.x + 6.28*ty) * 0.05;

然后我玩鼠标(打印它的位置),直到我足够接近以匹配您想要的输出,当时 tx=0.3477,ty=0.7812 这样您就可以硬编码

float x = sin( 25.0*p.y + 30.0*p.x + 6.28*0.3477) * 0.05;
float y = sin( 25.0*p.y + 30.0*p.x + 6.28*0.7812) * 0.05;