5.WebGL+Shader常用函数

不管我们是在做基于webgl的Cesium或threejs框架,在设计高级功能的时候,无一避免的就是shader编程,shader编程能让你的程序更加高效并且更加的绚丽。在我看来shader编程可以算是一种新的编程语言,只不过嵌入在各种引擎中,比如我们的webgl、opengl甚至unity或者ue,只不过后者都有自己的编程shader语言。

但是话说回来,shader的核心在于数学,里面不仅涉及到矩阵还有诸多函数的理解与应用。设想一下其实每个函数对应的函数图像,每个函数图像去操作像素点,进而拼凑出基于shader pixel的渲染效果。

abs()

函数意义

是求传入数字的绝对值,返回该数的绝对值

函数语法

float abs(float x)
vec2  abs(vec2 x)
vec3  abs(vec3 x)
vec4  abs(vec4 x)

函数值域

当x>0, abs(x)=x;
当x=0, abs(x)=0;
当x<0, abs(x)=-x;

很多书中没有具体解释abs对于vec2/vec3/vec4的理解,实际上和float是一样的。比如abs(vec2(x,y))将返回一个二维向量abs(x), abs(y)。其他的是同样的道理,大家自行体会哈。

函数图形

在这里插入图片描述

shader实例

下面代码是不用用abs()

precision mediump float;
uniform vec2  u_resolution;
void main(void)
{

    vec2 uv =(gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);
    vec3 col =vec3(uv.x);
    
    gl_FragColor = vec4(col,1.0);
}

在这里插入图片描述

若应用abs()

precision mediump float;
uniform vec2  u_resolution;
void main(void)
{
    vec2 uv =(gl_FragCoord.xy*2. - u_resolution.xy) / min(u_resolution.x, u_resolution.y);
    vec3 col =vec3(abs(uv.x));
    //vec3 col =vec3(abs(vec2(uv.x)),1.);
    gl_FragColor = vec4(col,1.0);
}

在这里插入图片描述

绘制圆环

1、解释下这个例子,m和p均为将坐标系进行标准化,具体前面我们已经详细讲解过。

2、1.-length§,此公式的意义在于求p点像素距离原点(0,0)的实际长度,当length§的具体为零的时候,圆心是最亮的。当向量模是1的时候,最黯淡,中间则是不断渐变的过程。代码如下所示:

precision mediump float;
uniform vec2  u_resolution;
void main(void){

    vec2 p = (gl_FragCoord.xy * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y);
    float t =1.- length(p);
    
    gl_FragColor = vec4(vec3(t), 1.0);
}

3、abs作用实际上是为了让我们最后形成的圆环,两边都对称发光,如果我们不使用的话,最后形成的圆环只能一边发光。我们具体分析下,当length§继续增大,1-length§必定是会小于0,但如果有了abs绝对值管理的话,当abs(1-length§),又会逐渐变亮。下面代码所产生的图像又印证我们的逻辑是完美的。

precision mediump float;

uniform vec2  u_resolution;

void main(void){

    vec2 p = (gl_FragCoord.xy * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y);
    
 
    float t =abs(.5- length(p));
    
    gl_FragColor = vec4(vec3(t), 1.0);
}

4、到了最后一步就是0.01除以某个值,因为当在1-length§,等于0的时候,0.01除于该值,其结果趋近去无穷大,所以在此时最亮。效果如下面代码所示:

precision mediump float;
uniform vec2  u_mouse;
uniform vec2  u_resolution;

void main(void){
    vec2 m =  (u_mouse.xy * 2.0 - u_resolution) / min(u_mouse.x, u_resolution.y);
    vec2 p = (gl_FragCoord.xy * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y);
    float t =0.01/ abs(1.- length(p-m));
    
    gl_FragColor = vec4(vec3(t), 1.0);
}

shader总结

abs()在实际对称图形中应用广泛,通过将uv x,y进行对称化,可以复制出相应的图形。

mod() 绘制格网

函数意义

用来求余数函数,返回两数相除的余数。当然具体也会分很多情况。

函数语法

float mod(float x, float y)  
vec2 mod(vec2 x, vec2 y)  
vec3 mod(vec3 x, vec3 y)  
vec4 mod(vec4 x, vec4 y)

vec2 mod(vec2 x, float y)  
vec3 mod(vec3 x, float y)  
vec4 mod(vec4 x, float y)

函数值域

一、两个异号整数求余

1.函数值符号规律(余数的符号)

mod(负,正)=正,mod(-x , y):所得到的值为正;

mod(正,负)=负,mod(x , -y):所得到的值为负;

结论:两个整数求余时,其值的符号为除数的符号。

2.取值规律 先将两个整数看作是正数,再做除法运算

①能整除时,其值为0 (或没有显示)

②不能整除时,其值=除数×(整商+1)-被除数

例:mod(36,-10)=-4 即:36除以10的整数商为3,加1后为4;其与除数之积为40;再与被除数之差为(40-36=4);取除数的符号。所以值为-4。

二、两个小数求余取值规律:

被除数-(整商×除数)之后在第一位小数位进行四舍五入。

例:mod(9,1.2)=0.6即:9除以1.2其整商为7;7与除数1.2之积为8.4;被除数9与8.4之差为0.6。故结果为0.6。

例:mod(9,2.2)=0.2 即:9除以2.2其整商为4;4与除数2.2这积为8.8;被除数9与8.8之差为0.2,故结果为0.2。

三、定义为被除数和除数先四舍五入,然后再相除求余数,被除数小于等于除数的整数取值规律:

例:mod(1,3)=1,mod(2,3)=2

函数图形

在这里插入图片描述

shader实例

案例一 绘制格网实现原理

1、绘制纵向格网。

precision mediump float;
uniform vec2  u_resolution;
void main(void)
{

vec2 uv =(gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	

    float a = mod(uv.x,1.);
gl_FragColor = vec4(vec3(a),1.0);
}

上述代码的实际意义是取uv.x除以2取余数,如上图所示,其中大家,观察图像可以发现,在uv.x取值范围在[0,2]的时候mod对应的值也是[0,2]。但是大家不要忘记,只要数值再等于1的时候就已经达到了最亮了。所以由此可以推算出从[0,1]之间是渐变到1。成像结果如下所示:

在这里插入图片描述

但是这个图像结果显然不是咱们想要的,我们不想要图像的渐变。所以这边采用了smoothstep函数的操作,smoothstep后面我们会给大家讲,它实际上是将线的宽度控制在[0,0.1]之间,当然这里面也可用if语句,提示一下:当mod函数值返回的值等于0的时候,d1等于0,其他时候都不等于0。但是shader里面运行if判断很影响效率,所以我们没有这样去做。
1.if判断

precision mediump float;
uniform vec2  u_resolution;
void main(void)
{
//vec2 uv =(gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
vec2 uv =(gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
float d1 =0.;  
    
    if(mod(uv.x,1.)<0.01){
     d1 = 1.;  
    }else{
        
    }

    float a = mod(uv.x,1.);
gl_FragColor = vec4(vec3(d1),1.0);
}

2.smoothstep方法


vec2 uv =(gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
float d1 =smoothstep(0.,0.1,mod(uv.x,1.));  
float a = mod(uv.x,1.);
gl_FragColor = vec4(vec3(d1),1.0);
}

2、绘制横向格网。

同样的道理,只不过换成操作y。

3、纵向与横向相互叠加。

相乘

precision mediump float;
uniform vec2  u_resolution;
void main(void)
{
//vec2 uv =(gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
vec2 uv =(gl_FragCoord.xy*30.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
float d1 =smoothstep(0.,0.1,mod(uv.x,1.));  
float d2 = smoothstep(0.,0.1,mod(uv.y,1.));
gl_FragColor = vec4(vec3(d2*d1),1.0);
}

在这里插入图片描述

floor()

函数意义

向下取整

函数语法

float floor(float x)  
vec2 floor(vec2 x)  
vec3 floor(vec3 x)  
vec4 floor(vec4 x)

函数值域

我们以最简单的float floor(float x) 举例:

floor(0.8)=0; 
floor(-0.8)=-1;

同样的道理vec2/vec3/vec4也是满足上述特点,拿floor(vec2 x)举例,返回的向量中的每个分量将具有以下值之一:

x各个分量向下取整。

例如:vec2(-6.1, 2.1),则floor(x)将返回vec2(-7.0, 2.0)

函数图形

在这里插入图片描述

shader实例

绘制实心格子

实现思路同ceil函数所举得例子,但是区别就是一个是向上取整,一个向下取整。向上取整,因为最大值更大于1,所以此时更加明亮。

#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
uniform vec2 u_resolution;
void main( void ) {
    vec2 uv =(gl_FragCoord.xy)/u_resolution.y;
    vec3 col = vec3(0.0);
    uv*=10.;
    vec2 id = floor(uv);//id.xy取值范围是[0,10]
    id.xy/=10.;//取值安慰变成[0,1]
    col.xy+=id; //所以渐变
    gl_FragColor = vec4(col,1.0);
}

在这里插入图片描述

fract()

函数意义

fract函数的意思是取小数部分,即x-floor(x),例如fract(1.5) = 0.5fract(-1.2)=0.8。这里fract(t)mod(t,1.0)是等价的。

函数语法

float fract(float x)
vec2  fract(vec2 x)
vec3  fract(vec3 x)
vec4  fract(vec4 x)

函数值域

我们以最简单的float fract(float x) 举例:

fract(1.8)=0.8;
fract(-1.8)=0.2;

同样的道理vec2/vec3/vec4也是满足上述特点,用于计算向量 x 中每个分量的小数部分。函数 fract 都会将其小数部分提取出来,并返回一个新的向量,其中每个分量的值都为原始向量中对应分量的小数部分。

例如,如果输入向量 xvec2(3.14, 1.11),那么 fract(x) 的返回值将是 vec2(0.14, 0.11)。那如果输入向量 xvec2(-3.1, -1.1),那么 fract(x) 的返回值将是 vec2(-3.1-floor(-3.1), -1.1-floor(-1.1)),vec2(-3.1+4, -1.1+2),vec2(0.9, 0.9)

函数图形

在这里插入图片描述

shader实例

飘动的小球儿

简单讲解下该例子:

1、关键函数是MovingCeil。

(1) float time = iTime*0.2;目的是降低时间变化速率。

(2)fract(time)>0.5,表示的是时间变化的后0.5秒。具体大家可以看下图图像。由图像可以看出整个周期是那个斜着的直线,这个直线的最大值就是1.,所以它的0.5倍正好是每个周期中时间走到中间的时候。也就是后半段时间

在这里插入图片描述
(3)fract( uv.y * 0.5) > 0.5,该函数判断是用于对格子进行分组表示,目前可以确定的是每两行格子带为一组,咱们还是可以根据下面图像找到本质原因
在这里插入图片描述

当我们的fract( uv.y * 0.5) =0.5的时候恰好对应的是红色圆框标记的地方,所对应的是两个格子的中间点,由此可得当fract( uv.y * 0.5) >0.5对应的是第二个格子,fract( uv.y * 0.5) <0.5,对应的是第一个格子。

(4) uv.x += fract(time)*2.0;。这个是根据时间去动态改变格子的横坐标。

其他的情况是类似于上述讲解的,大家请认真思考体会。

2、 float d = length(uv);此函数是sdf圆的函数式子,目的是将格网变成一个个小圆形。

3、smoothstep(0.3,0.28,d);该函数我们后面会讲解,主要是将其圆边线变得更加明显。

#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
uniform vec2 u_resolution;
vec2 MovingCeil(vec2 uv){
    float time = u_time*0.2;
    if( fract(time)>0.5 ){
        if (fract( uv.y * 0.5) > 0.5){
            uv.x += fract(time)*2.0;
        } else {
            uv.x -= fract(time)*2.0;
        }
    } else {
        if (fract( uv.x * 0.5) > 0.5){
            uv.y += fract(time)*2.0;
        } else {
            uv.y -= fract(time)*2.0;
        }
    }
    return fract(uv);
}
void main( void ) {
    
    vec2 uv = (gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
    vec3 col = vec3(0.0);
    uv*=10.;
    
    uv=MovingCeil(uv)-.5;
    float d = length(uv);
    col+=smoothstep(0.3,0.28,d);

    gl_FragColor = vec4(col,1.0);
}

在这里插入图片描述

min()

函数意义

返回两个值最小的一个

函数语法

float min(float x, float y)  
vec2 min(vec2 x, vec2 y)  
vec3 min(vec3 x, vec3 y)  
vec4 min(vec4 x, vec4 y)

vec2 min(vec2 x, float y)  
vec3 min(vec3 x, float y)  
vec4 min(vec4 x, float y)

函数值域

一、我们以最简单的float min(float x, float y) 举例:

min(1.5, 0.5) =0.5;
min(-1.5, -0.5) =-1.5;
min(x, 0.) ={x<0 返回x;x大于等于0返回0

二、min(vec4 x, vec4 y)

用于比较两个 vec4 类型的向量,返回它们中每个分量的最小值,即分别比较 x 向量和 y 向量中对应位置上的分量大小,并将每个位置上较小的值作为输出向量的对应分量。

例如,如果 xvec4(1.0, 2.0, 3.0, 4.0),而 yvec4(2.0, 1.0, 4.0, 3.0),则 min(x, y) 的结果将是 vec4(1.0, 1.0, 3.0, 3.0)

三、vec2 min(vec2 x, float y)

用于比较 vec2 类型的向量,返回它们中每个分量与float y,值的大小,返回相比较的最小值。例如:min(vec2(1.0, 2.0),1.5),返回的结果是vec2(1.0, 1.5)

函数图形

在这里插入图片描述

shader实例

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

void main(){
    
    vec2 uv=(gl_FragCoord.xy*2.-u_resolution.xy)/min(u_resolution.x,u_resolution.y);
    float d1=step(length(uv-vec2(.1)),.3);
    float d2=step(length(uv-vec2(.2)),.3);
    float d=min(d1,d2);
    gl_FragColor=vec4(vec3(d),1.);
}

在这里插入图片描述

https://www.icegl.cn/ask/article/12.html

max()

函数意义

返回两个值最大的一个

函数语法

float max(float x, float y)  
vec2 max(vec2 x, vec2 y)  
vec3 max(vec3 x, vec3 y)  
vec4 max(vec4 x, vec4 y)

vec2 max(vec2 x, float y)  
vec3 max(vec3 x, float y)  
vec4 max(vec4 x, float y)

函数值域

一、我们以最简单的float max(float x, float y) 举例:

max(1.5, 0.5) =1.5;
max(-1.5, -0.5) =-0.5;
max(x, 0.) ={x<0 返回0;x大于等于0返回x

二、max(vec4 x, vec4 y)

用于比较两个 vec4 类型的向量,返回它们中每个分量的最大值,即分别比较 x 向量和 y 向量中对应位置上的分量大小,并将每个位置上较小的值作为输出向量的对应分量。

例如,如果 xvec4(1.0, 2.0, 3.0, 4.0),而 yvec4(2.0, 1.0, 4.0, 3.0),则 min(x, y) 的结果将是 vec4(2.0, 2.0, 4.0, 4.0)

三、vec2 max(vec2 x, float y)

用于比较 vec2 类型的向量,返回它们中每个分量与float y,值的大小,返回相比较的最大值。例如:max(vec2(1.0, 2.0),1.5),返回的结果是vec2(1.5, 2.0)

函数图形

shader实例

  vec2 uv=(gl_FragCoord.xy*2.-u_resolution.xy)/min(u_resolution.x,u_resolution.y);
    float d1=step(length(uv-vec2(.1)),.3);
    float d2=step(length(uv-vec2(.2)),.3);
    float d=max(d1,d2);
    gl_FragColor=vec4(vec3(d),1.);

在这里插入图片描述

clamp()

函数意义

返回第二个数和第三个数中间的数,一般我们常用返回[0,1]之间的数值。可以使用 clamp 函数将不断增加、减小或随机变化的数值限制在一系列的值中。

函数语法

float clamp(float x, float minVal, float maxVal)  
vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)  
vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal)  
vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal)

vec2 clamp(vec2 x, float minVal, float maxVal)  
vec3 clamp(vec3 x, float minVal, float maxVal)  
vec4 clamp(vec4 x, float minVal, float maxVal)

函数值域

一、我们以最简单的float clamp(float x, float minVal, float maxVal) 举例:

clamp(22,4,6)=6;
clamp(2,4,6)=4;
clamp(5,4,6)=5;

二、vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)

用于将一个二维向量 x 中的每个分量限制在指定的范围内,并返回一个新的向量,其中每个分量都被限制在 minValmaxVal 指定的范围内。

例如,如果 xvec2(1.5, 2.5)minValvec2(1.0, 2.0),而 maxValvec2(2.0, 3.0),则 clamp(x, minVal, maxVal) 的结果将是 vec2(1.5, 2.5),因为 x 的每个分量都在指定的范围内。

如果 x 的某个分量小于 minVal 对应的分量,则该分量将被限制为 minVal 对应的分量值;如果 x 的某个分量大于 maxVal 对应的分量,则该分量将被限制为 maxVal 对应的分量值。换句话说,clamp 函数可以确保向量中的每个分量都位于指定的范围内。

三、vec2 clamp(vec2 x, float minVal, float maxVal)

vec2 clamp(vec2 x, float minVal, float maxVal) 等价于vec2 clamp(vec2 x, vec2(minVal),vec2(maxVal))

函数图形

在这里插入图片描述

shader实例

float sdSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 ap = p-a, ab = b-a;
    float k = clamp( dot(ap,ab)/dot(ab,ab), 0.0, 1.0 );
    return length( ap - ab*k );
}
void main(){
    
    vec2 uv=(gl_FragCoord.xy*2.-u_resolution.xy)/min(u_resolution.x,u_resolution.y);
    float d1=step(sdSegment(uv,vec2(0.1),vec2(0.8)),0.03);
    gl_FragColor=vec4(vec3(d1),1.);
}

https://www.icegl.cn/ask/article/6.html

mix()

函数意义

Mix()在x和y之间使用a为权重执行线性插值。

函数语法


    float mix(float x, float y, float a)
 
    vec2 mix(vec2 x, vec2 y, vec2 a) 
    vec3 mix(vec3 x, vec3 y, vec3 a) 
    vec4 mix(vec4 x, vec4 y, vec4 a) 

    vec2 mix(vec2 x, vec2 y, float a) 
    vec3 mix(vec3 x, vec3 y, float a) 
    vec4 mix(vec4 x, vec4 y, float a)

函数值域

一、我们以最简单的float mix(float x, float y, float a) 举例:

mix(2,4,0.6)=3.2;
mix(5,7,0.5)=6;

函数图形

在这里插入图片描述

shader实例

渐变的颜色

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

uniform vec2 u_resolution;


vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);



void main() {
    vec2 st = (gl_FragCoord.xy*2.-  u_resolution.xy) / min(u_resolution.x, u_resolution.y);	
    vec3 color = vec3(0.0);
    vec3 pct = vec3(st.x);
    color = mix(colorA, colorB, pct);
    gl_FragColor = vec4(color,1.0);
}

smoothstep()

函数意义

此函数应用返回比较广,因为他具有在[0,1]间过渡的能力,并且过渡具有渐进的特性,应用此特性我能做很多事情。 常用的场景是抗锯齿

函数语法

float smoothstep(float edge0, float edge1, float x)  
vec2 smoothstep(vec2 edge0, vec2 edge1, vec2 x)  
vec3 smoothstep(vec3 edge0, vec3 edge1, vec3 x)  
vec4 smoothstep(vec4 edge0, vec4 edge1, vec4 x)

vec2 smoothstep(float edge0, float edge1, vec2 x)  
vec3 smoothstep(float edge0, float edge1, vec3 x)  
vec4 smoothstep(float edge0, float edge1, vec4 x)

函数值域

此函数需要传入三个参数,前两个参数是边界参数,最后一个是输入的x值。smoothstep返回的结果分为以下两种情况:

情况一

如果参数一大于参数二,函数成像如下图所示:

在这里插入图片描述

当x小于参数二的时候,返回结果1。当x大于参数一的时候,返回结果0.

情况二

如果参数二大于参数一的时候,函数成像如下图所示:

在这里插入图片描述

当x小于参数一的时候,返回结果0。当x大于参数二的时候,返回结果1.

函数图形

如上图所示

shader实例

#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
float band(float t, float start,float end, float blur){

float d1 = smoothstep(start-blur,start+blur,t);
float d2 = smoothstep(end+blur,end-blur,t);
return d1*d2;

}

float rect(float x,float y,float startx,float endx,float starty,float endy,float blur1){
float rect1 = band(x,startx,endx,blur1);
float rect2 = band(y,starty,endy,blur1);
return rect1 * rect2;
}
void main() {
   
    vec2 uv = gl_FragCoord/u_resolution.xy;
    float t = u_time;
    uv-=.5;
    uv.x *=u_resolution.x/u_resolution.y;
    float x = uv.x;
    float y = uv.y;
    vec3 col =vec3(rect(x,y,-0.5 ,0.5,-0.1,0.1,0.001)) ;

    gl_FragColor = vec4(col,1.0);
}

在这里插入图片描述

sin()

函数意义

sin()函数又叫做正弦函数,我们在初中数学中经常用到…

函数语法

float sin(float angle)  
vec2 sin(vec2 angle)  
vec3 sin(vec3 angle)  
vec4 sin(vec4 angle)

函数值域

值域范围是[-1,1]

函数图形

在这里插入图片描述

shader实例

precision mediump float;
uniform vec2  u_resolution;
uniform float  u_time;
void main( void ) {
    // 将像素坐标归一化(区间 [0.0, 1.0])
    // iResolution 是 Shadertoy 提供的视口分辨率全局变量(类型:vec3)
    vec2 uv = gl_FragCoord .xy/ u_resolution.xy;
    // 振幅(控制波浪顶端和底端的高度)
    float amplitude = 0.05;
    // 角速度(控制波浪的周期)
    float angularVelocity = 10.0;
    // 频率(控制波浪移动的速度)
    float frequency = 10.0;
    // 偏距(设为 0.5 使得波浪垂直居中于屏幕)
    float offset = 0.5;
    // 初相位(正值表现为向左移动,负值则表现为向右移动)
    // iTime 是 Shadertoy 提供的运行时间全局变量(类型:float)
    float initialPhase = frequency * u_time;
    // 代入正弦曲线公式计算 y 值
    // y = Asin(ωx ± φt) + k
    float y = amplitude * sin((angularVelocity * uv.x) + initialPhase) + offset ;
    // 区分 y 值上下部分,设置不同颜色
    vec4 color = uv.y > y ? vec4(0.0, 0.0, 0.0, 1.0) : vec4(0.0, 0.7, 0.9, 1.0);
    // 输出到屏幕
    gl_FragColor = color;
}

在这里插入图片描述

tan()

tan()函数又叫做正切函数,我们在初中数学中经常用到…在直角三角形ABC中,∠C = 90,Tan(A)=BC/AC

在这里插入图片描述

函数语法

float tan(float angle)  
vec2 tan(vec2 angle)  
vec3 tan(vec3 angle)  
vec4 tan(vec4 angle)

函数值域

值域范围是[-无穷,+无穷]

函数图形

在这里插入图片描述

shader实例

绘制等高线

shader总结

f(x)=Atan(ωx+φ);

周期是T=π/|ω|
函数图像(以 x 轴为基线)拉伸到原来的A倍
沿X轴方向平移φ个单位而已

shader总结

sin函数最显著的特点是在[-1,1]中不断震荡,更加直白来说就是在随着x值不断变化,sin函数返回的值循环变化, 充分利用这一特性可以做出很多有意思的图形。此外还需要注意sin函数可以进行以下变形

A*sin(w*x+m)

首先A改变的是sin函数的值域范围,w改变的是sin函数的周期,m改变边的sin函数的位移范围。具体大家可以在我之前推荐的函数绘图工具中查看。

atan()

反正切函数。反正切函数为正切函数y=tanx(x∈[-½π,½π])的反函数。tan是正切函数,atan是反正切函数,在(-π/2,π/2)区间内,两者互为反函数,即若-π/2<x<π/2,且y=tan(x),则x=atan(y),或atan(tan(x))=xhttps://qa.1r1g.com/sf/ask/1824928731/

在这里插入图片描述

函数语法

float atan(float y, float x)  
vec2 atan(vec2 y, vec2 x)  
vec3 atan(vec3 y, vec3 x)  
vec4 atan(vec4 y, vec4 x)

float atan(float y_over_x)  
vec2 atan(vec2 y_over_x)  
vec3 atan(vec3 y_over_x)  
vec4 atan(vec4 y_over_x)

函数值域

如果传入的参数是这样的,atan的值域是(-π/2,π/2)。

float angle = atan(uv.y/uv.x);

如果传入的参数是这样的,atan的值域是(-π,π)。

float atan(float y, float x)  

函数图形

在这里插入图片描述

shader实例

绘制旋转的风车

简单讲一下这个实例,

precision mediump float;
uniform float u_time;
uniform vec2  u_mouse;
uniform vec2  u_resolution;

void main(void){

vec2 p = (gl_FragCoord.xy -.5 *  u_resolution) / min(u_resolution.x, u_resolution.y);
float t = atan(p.y, p.x) +u_time;
t = sin(t * 10.0);
float d =smoothstep(0.41,0.4,length(p));
gl_FragColor = vec4(vec3(.1/t*d  ), 1.0);
}

在这里插入图片描述

length()

函数意义

用来计算向量的长度,实际上我们在初级课程的时候给大家提到了该方法。等价于 pct = sqrt(tC.x*tC.x+tC.y*tC.y);

函数语法

传入的向量值。

float length(float x)  
float length(vec2 x)  
float length(vec3 x)  
float length(vec4 x)

函数值域

函数图形

shader实例

绘制圆形

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
	vec2 st = gl_FragCoord.xy/u_resolution;
    float pct = 0.0;

    vec2 toCenter = vec2(0.5)-st;
    pct = length(toCenter);

    vec3 color = vec3(pct);

	gl_FragColor = vec4( color, 1.0 );
}

在这里插入图片描述

dot()

函数意义

计算两个向量的点积,是计算两个向量之间的夹角余弦值

函数语法

输入的参数分别是第一个向量和第二个向量

float dot(float x, float y)  
float dot(vec2 x, vec2 y)  
float dot(vec3 x, vec3 y)  
float dot(vec4 x, vec4 y)

函数值域

函数图形

shader实例

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // 定义表面法线
    vec3 normal = normalize(vec3(0.5, 1.0, 0.5));
    
    // 定义光源方向
    vec3 lightDir = normalize(vec3(0.2, 0.5, 0.9));
    
    // 计算夹角余弦值
    float cosTheta = dot(normal, lightDir);
    
    // 输出颜色
    fragColor = vec4(cosTheta, cosTheta, cosTheta, 1.0);
}

pow()

函数意义

用来求几次方,一般传入两个参数,具体大家看看下面函数的用法吧。

函数语法

第一个参数是指数函数下面的那个数字,学术称为底数。第二个参数是指数函数右上角的角标,学术称为指数。

   float pow(float x, float y) 
   vec2 pow(vec2 x, vec2 y) 
   vec3 pow(vec3 x, vec3 y) 
   vec4 pow(vec4 x, vec4 y)

函数值域

需要分情况讨论:

如果是偶数函数的值域范围是[0,+无穷]

如果是奇数函数的值域范围是[-无穷,+无穷]

函数图形

在这里插入图片描述
在这里插入图片描述

shader实例

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
void main(){
	vec2 uv =  gl_FragCoord.xy/u_resolution;
    
    // animation
    vec2 sunVec;
    if (u_mouse.x<20.0)
    {
     	sunVec = vec2((0.8+0.5 * sin(u_time)),( 0.44 + 0.4 * cos( 2.0 * u_time)));
    }else{
    	sunVec = u_mouse.xy/u_resolution.y;
    }
     	
   
    //Mie mask
    float sun = max(1.0 - (1.0 + 10.0 * sunVec.y + 1.0 * uv.y) * length(uv - sunVec),0.0)
        + 0.3 * pow(1.0-uv.y,12.0) * (1.6-sunVec.y);
	
    //the sauce
    gl_FragColor = vec4(mix(vec3(0.3984,0.5117,0.7305), vec3(0.7031,0.4687,0.1055), sun)
              * ((0.5 + 1.0 * pow(sunVec.y,0.4)) * (1.5-uv.y) + pow(sun, 5.2)
              * sunVec.y * (5.0 + 15.0 * sunVec.y)),1.0);
    
    //fragColor = vec4(sun);
   
}

在这里插入图片描述

shader总结

pow函数实际上很灵活,指数是奇数还是偶数对于函数图形结果影响很大,如果是偶数的话是关于y轴的轴对称图形,如果是奇数的话关于原点的中心对称图形。

小结

章节复习

练习一下课程中案例,并结合图形体会其函数图像变化。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebGL是一种用于在Web浏览器中绘制3D图形的技术。要绘制多边形,你需要定义顶点数据,并使用WebGL的绘制函数将其绘制到画布上。 以下是一个使用WebGL绘制三角形的示例: ```javascript // 获取画布元素 var canvas = document.getElementById("myCanvas"); // 获取WebGL上下文 var gl = canvas.getContext("webgl"); // 定义顶点数据 var vertices = [ -0.5, -0.5, 0.0, // 第一个顶点的坐标 0.5, -0.5, 0.0, // 第二个顶点的坐标 0.0, 0.5, 0.0 // 第三个顶点的坐标 ]; // 创建缓冲区对象 var vertexBuffer = gl.createBuffer(); // 绑定缓冲区对象 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // 将顶点数据写入缓冲区对象 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // 获取顶点着色器代码 var vertexShaderSource = ` attribute vec3 aPosition; void main() { gl_Position = vec4(aPosition, 1.0); } `; // 创建顶点着色器对象 var vertexShader = gl.createShader(gl.VERTEX_SHADER); // 编译顶点着色器代码 gl.shaderSource(vertexShader, vertexShaderSource); gl.compileShader(vertexShader); // 获取片元着色器代码 var fragmentShaderSource = ` void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `; // 创建片元着色器对象 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); // 编译片元着色器代码 gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); // 创建着色器程序对象 var program = gl.createProgram(); // 将顶点着色器和片元着色器附加到着色器程序对象上 gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); // 链接着色器程序对象 gl.linkProgram(program); // 使用着色器程序对象 gl.useProgram(program); // 获取顶点位置属性的位置 var aPosition = gl.getAttribLocation(program, "aPosition"); // 启用顶点属性数组 gl.enableVertexAttribArray(aPosition); // 指定顶点属性数组的数据格式和位置 gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0); // 清空画布 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 绘制三角形 gl.drawArrays(gl.TRIANGLES, 0, 3); // 刷新画面 gl.flush(); ``` 这段代码首先获取了画布元素和WebGL上下文,然后定义了三个顶点的坐标数据。接下来,创建了顶点着色器和片元着色器,并编译它们的代码。然后,创建了着色器程序对象,并将顶点着色器和片元着色器附加到着色器程序对象上。之后,启用顶点属性数组,并指定顶点属性数组的数据格式和位置。最后,清空画布,绘制三角形,并刷新画面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值