GLSL 片段着色器中的曲线旋转
Curve rotation in GLSL fragment shader
我正在尝试找出正确的数学来旋转和平移片段着色器中显示的曲线。
我试图完成的是在局部坐标系中定义一条曲线,例如正弦曲线,旋转它然后平移它。像这样:
这是在 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 with the following code (can also be seen interactively 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
设置为 false
或 true
.
来切换。
第一种模式尝试使用函数 plot(
) & plotTransformed()
进行绘图。
第二种模式根据与计算出的曲线坐标的距离为片段设置颜色。
use_plot_function
设置为 true
的第一个模式的结果:
use_plot_function
设置为 false
的第二种模式的结果:
显然我误解了在片段着色器中应该如何完成。
我应该如何在 GLSL 片段着色器中正确定义变换曲线?
懒得看你的代码对于简单的 sinwave 来说太复杂了,但是在顶点着色器中旋转要简单得多,但是如果你坚持使用片段着色器,我会:
用基向量定义旋转坐标系
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;
参数
对于我们通常使用参数的任何曲线。在您的情况下,它是正弦波的角度 wjich 也是旋转坐标的 x 坐标离子(等于 some_constant0 + u*some_constant1
)。
片段中的参数曲线
所以当我们有参数时,我们只计算曲线点的 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);
}
//---------------------------------------------------------------------------
如您所见,我使用了硬编码的颜色和角度。您可以使用 uniform 更改角度,使用 uniform 更改颜色,或者使用 VBO/VAO 或 glColor
...
中的直接颜色
如果你想围绕屏幕中心旋转,我使用了 O(0.5,0.5)
然后使用 O(0.0,0.0)
代替 ...
我正在尝试找出正确的数学来旋转和平移片段着色器中显示的曲线。
我试图完成的是在局部坐标系中定义一条曲线,例如正弦曲线,旋转它然后平移它。像这样:
这是在 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 with the following code (can also be seen interactively 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
设置为 false
或 true
.
第一种模式尝试使用函数 plot(
) & plotTransformed()
进行绘图。
第二种模式根据与计算出的曲线坐标的距离为片段设置颜色。
use_plot_function
设置为 true
的第一个模式的结果:
use_plot_function
设置为 false
的第二种模式的结果:
显然我误解了在片段着色器中应该如何完成。
我应该如何在 GLSL 片段着色器中正确定义变换曲线?
懒得看你的代码对于简单的 sinwave 来说太复杂了,但是在顶点着色器中旋转要简单得多,但是如果你坚持使用片段着色器,我会:
用基向量定义旋转坐标系
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;
参数
对于我们通常使用参数的任何曲线。在您的情况下,它是正弦波的角度 wjich 也是旋转坐标的 x 坐标离子(等于
some_constant0 + u*some_constant1
)。片段中的参数曲线
所以当我们有参数时,我们只计算曲线点的 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);
}
//---------------------------------------------------------------------------
如您所见,我使用了硬编码的颜色和角度。您可以使用 uniform 更改角度,使用 uniform 更改颜色,或者使用 VBO/VAO 或 glColor
...
如果你想围绕屏幕中心旋转,我使用了 O(0.5,0.5)
然后使用 O(0.0,0.0)
代替 ...