使用着色器绘制图案
What is UV
在创建着色器时,我们需要绘制特定的图案,如星星、圆圈、光透镜、波浪等。虽然可以使用纹理,但绘制形状可以让我们有更多的控制权;我们可以对形状参数进行动画处理,并且没有要加载的纹理。需要在fragment shader中用到uv坐标。
什么是uv坐标?
UV坐标是用来将2D纹理贴图到3D模型表面的坐标系。这里的U和V代表的是2D空间的两个轴,类似于平面中的X和Y,但为了避免与3D空间中的X、Y、Z混淆,所以用U和V来命名。
在Threejs中UV坐标的范围是0到1,覆盖整个纹理图像。原点(0,0)在左下角,而(1,1)在右上角。uv坐标的count取决于material的分段数
如何将uv坐标发送到fragment shader?
fragment shader无法直接访问顶点数据,所以我们需要通过 vertex shader 创建vUv变量发送给 fragment shader。
varying vec2 vUv;
void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vUv = uv;}
// fragment.glslvarying vec2 vUv;
void main(){ // use vUv gl_FragColor = vec4(vUv, 1.0, 1.0)}Pattern 1
直接使用 uv 坐标,原点的颜色为(0.0, 0.0, 1.0, 1.0)到右上角(1.0, 1.0, 1.0, 1.0)的渐变
varying vec2 vUv;
void main() { gl_FragColor = vec4(vUv, 1.0, 1.0);}
Pattern 2
同样的使用 uv 坐标,但b设置为0.0,为(0.0, 0.0, 0.0, 1.0)到右上角(1.0, 1.0, 0.0, 1.0)的渐变
void main() { gl_FragColor = vec4(vUv, 0.0, 1.0);}
Pattern 3
只使用 uv 坐标的x值,得到一个在x轴上的黑白渐变。颜色在x轴上y轴上每个点都是(0.0, 0.0, 0.0, 1.0)到(1.0, 1.0, 1.0, 1.0)变化
varying vec2 vUv;
void main(){ float strength = vUv.x; gl_FragColor = vec4(vec3(strength), 1.0);}
Pattern 4
和 pattern 3 完全相同,不过应用在y轴上。
varying vec2 vUv;
void main(){ float strength = vUv.y; gl_FragColor = vec4(vec3(strength), 1.0);}
Pattern 5 (1.0 - …)
将 pattern 4 的值通过 1.0 - ...反转。(1.0, 1.0, 1.0, 1.0) 到(0.0, 0.0, 0.0, 1.0)变化
varying vec2 vUv;
void main(){ float strength = 1.0 - vUv.y; gl_FragColor = vec4(vec3(strength), 1.0);}
Pattern 6
将 uv.y 乘以 10,gl_FragColor将很快的到达(1.0, 1.0, 1.0, 1.0),后面大于1.0也显示白色
varying vec2 vUv;
void main(){ float strength = vUv.x; gl_FragColor = vec4(vec3(strength), 1.0);}
Pattern 7 mod(…)
通过取模运算 mod(...),重复某一段的变化。
将y的值乘以10.0,模1.0,分为10段,每段都是(0.0, 0.0, 0.0, 1.0)到(1.0, 1.0, 1.0, 1.0)变化。如果模0.5,最大值为0.5,结果就是(0.0, 0.0, 0.0, 1.0)到(0.5, 0.5, 0.5, 1.0)
varying vec2 vUv;
void main(){ float strength = mod(vUv.y * 10.0, 1.0); gl_FragColor = vec4(vec3(strength), 1.0);}
Pattern 8 step(…)
在 pattern 7 的基础上实现斑马条纹,如果颜色在0.5以下,设为0.0;在0.5以上,设置1.0。
可以通过if()判断实现,但是会影响性能。
varying vec2 vUv;
void main(){ float strength = mod(vUv.y * 10.0, 1.0); if(strength < 0.5) { strength = 0.0; } else { strength = 1.0; } // or strength = strength < 0.5 ? 0.0 : 1.0; // 三元运算符 gl_FragColor = vec4(vec3(strength), 1.0);}glsl 提供了step(edge, x)函数。
- 参数:
edge是阈值,x是输入值。 - 返回值:若
x >= edge返回1.0,否则返回0.0。
varying vec2 vUv;
void main(){ float strength = mod(vUv.y * 10.0, 1.0); strength = step(0.5, strength); // 超过阈值0.5,返回1.0,否则返回0.0 gl_FragColor = vec4(vec3(strength), 1.0);}
Pattern 9
通过调整 step()的阈值,更改条纹的粗细。
float strength = mod(vUv.y * 10.0, 1.0);strength = step(0.8, strength);
Pattern 10
将 pattern 9 中修改vUv.x,在x轴上分段(竖条纹)
float strength = step(0.8, mod(vUv.x * 10.0, 1.0));
Pattern 11 +
将 pattern 9 和 pattern 10 的图案叠加,可以使用+取并集
float strength = step(0.8, mod(vUv.x * 10.0, 1.0));strength += step(0.8, mod(vUv.y * 10.0, 1.0));
Pattern 12 *
将 pattern 9 和 pattern 10 的图案取相交处,可以使用*取交集
float strength = step(0.8, mod(vUv.x * 10.0, 1.0));strength *= step(0.8, mod(vUv.y * 10.0, 1.0)); // 只能看到交点处
Pattern 13
在 pattern 12 上,将x轴上的分段阈值缩小,x轴方向上条纹更粗。然后取交集。
float strength = step(0.4, mod(vUv.x * 10.0, 1.0));strength *= step(0.8, mod(vUv.y * 10.0, 1.0));
Pattern 14
将 pattern 13 的图案分别按照水平方向(barX)和竖直方向(barY)实现,然后再取并集。
float barX = step(0.4, mod(vUv.x * 10.0, 1.0));barX *= step(0.8, mod(vUv.y * 10.0, 1.0));float barY = step(0.4, mod(vUv.y * 10.0, 1.0));barY *= step(0.8, mod(vUv.x * 10.0, 1.0));float strength = barX + barY;
Pattern 15
基于 pattern 14 将 barX 和 barY 分别沿各自的方向偏移0.2,得到 + 图案
float barX = step(0.4, mod(vUv.x * 10.0, 1.0));barX *= step(0.8, mod(vUv.y * 10.0 + 0.2, 1.0));float barY = step(0.4, mod(vUv.y * 10.0, 1.0));barY *= step(0.8, mod(vUv.x * 10.0 + 0.2, 1.0));float strength = barX + barY;
Pattern 16 abs()
绘制x轴方向,0.5到0.0到0.5的渐变。abs()取绝对值
float strength = abs(vUv.x - 0.5);
Pattern 17 min()
在水平方向和竖直方向取图案的最小值min()
float strength = min(abs(vUv.x - 0.5), abs(vUv.y - 0.5));
Pattern 18 max()
在水平方向和竖直方向取最大值max()
float strength = max(abs(vUv.x - 0.5), abs(vUv.y - 0.5));
Pattern 19
基于 pattern 18 ,使用 step()分段显示,实现矩形环。
float strength = max(abs(vUv.x - 0.5), abs(vUv.y - 0.5));strength = step(0.2, strength);
Pattern 20
基于 pattern 19,通过取反(1.0 - ...)操作实现矩形square2,再与square1相乘取交集,构成更细的矩形环。
float square1 = step(0.2, max(abs(vUv.x - 0.5), abs(vUv.y - 0.5)));float square2 = 1.0 - step(0.25, max(abs(vUv.x - 0.5), abs(vUv.y - 0.5)));float strength = square1 * square2;
Pattern 21 floor()
水平方向的渐变条纹,floor()向下取整,分成x段就乘x除x。
float strength = floor(vUv.x * 10.0) / 10.0;
Pattern 22
基于 pattern 21, 将水平方向和竖直方向取交集得到,对角线渐变的条纹
float strength = floor(vUv.x * 10.0) / 10.0;strength *= floor(vUv.y * 10.0) / 10.0;
Pattern 23 random()
目的是每个 uv 坐标的颜色都取随机数。但是 glsl 中没有内置的随机数方法,只能通过自定义函数得到一个看起来随机的[0, 1]的数。下面的方法通过接收 vec2返回一个相对随机的值。
float random(vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);}在main()中使用,形成雪花屏。因为random()是固定的,每次渲染都是相同的图案。
float strength = random(vUv);
Pattern 24
基于 pattern 22 和 23,每个区域的坐标都取随机值。
vec2 gridUv = vec2(floor(vUv.x * 10.0) / 10.0, floor(vUv.y * 10.0) / 10.0);float strength = random(gridUv);
Pattern 25
基于 Pattern 24, 给竖直方向设置x偏移
vec2 gridUv = vec2(floor(vUv.x * 10.0) / 10.0, floor(vUv.y * 10.0 + vUv.x * 5.0) / 10.0);float strength = random(gridUv);
Pattern 26 length()
根据uv坐标上该点的模长。原点的值 0.0 到右上角的 √2
float strength = length(vUv);
Pattern 27 distance()
计算uv坐标中各点,到(0.5, 0.5)的距离,从(0.5,0.5)的 0.0 到顶点的 √0.5
float strength = distance(vUv, vec2(0.5));等价于向量减去0.5的模长。
float strength = length(vUv - 0.5);
Pattern 28
基于 pattern 27 取反
float strength = 1.0 - distance(vUv, vec2(0.5));
Pattern 29
基于 pattern 27,坐标距离(0.5, 0.5)越近,strength的值越大。可以做星星效果,更改分子的值。但是平面的边缘不等于0,可能在特定的角度可以看到平面
float strength = 0.015 / distance(vUv, vec2(0.5)) + 0.1;
Pattern 30
在 pattern 29 上,变换vu坐标
vec2 lightUv = vec2(vUv.x * 0.1 + 0.45, vUv.y * 0.5 + 0.25);float strength = 0.015 / distance(lightUv, vec2(0.5));
Pattern 31
将 pattern 30 的水平方向和竖直方向取交集,构成十字
vec2 lightUvX = vec2(vUv.x * 0.1 + 0.45, vUv.y * 0.5 + 0.25);float lightX = 0.015 / distance(lightUvX, vec2(0.5));vec2 lightUvY = vec2(vUv.y * 0.1 + 0.45, vUv.x * 0.5 + 0.25);float lightY = 0.015 / distance(lightUvY, vec2(0.5));float strength = lightX * lightY;
Pattern 32 rotate()
将 pattern 31 旋转。
glsl 中没有内置旋转的函数,通过自定义rotate()实现
参数: uv:uv坐标,rotation:旋转角度,mid:旋转中心点
#define PI 3.1415926535897932384626433832795
vec2 rotate(vec2 uv, float rotation, vec2 mid) { return vec2( cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x, cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y );}main()旋转45°
vec2 rotatedUv = rotate(vUv, PI * 0.25, vec2(0.5));vec2 lightUvX = vec2(rotatedUv.x * 0.1 + 0.45, rotatedUv.y * 0.5 + 0.25);float lightX = 0.015 / distance(lightUvX, vec2(0.5));vec2 lightUvY = vec2(rotatedUv.y * 0.1 + 0.45, rotatedUv.x * 0.5 + 0.25);float lightY = 0.015 / distance(lightUvY, vec2(0.5));float strength = lightX * lightY;Pattern 33
基于 pattern 27,取阈值,可以得到圆形
float strength = step(0.25, distance(vUv, vec2(0.5)));
pattern 34
基于 pattern 27 取绝对值
float strength = abs(distance(vUv, vec2(0.5)) - 0.25);
Pattern 35
pattern 33 与 pattern 34 结合,得到圆环
float strength = step(0.01, abs(distance(vUv, vec2(0.5)) - 0.25));
Pattern 36
pattern 35 取反
float strength = 1.0 - step(0.01, abs(distance(vUv, vec2(0.5)) - 0.25));
Pattern 37
将 pattern 36 的圆环,设置竖直方向的变化
vec2 wavedUv = vec2(vUv.x, vUv.y + sin(vUv.x * 30.0) * 0.1);float strength = 1.0 - step(0.01, abs(distance(wavedUv, vec2(0.5)) - 0.25));
Pattern 38
将 pattern 37 同样设置水平和竖直的变化
vec2 wavedUv = vec2(vUv.x + sin(vUv.y * 30.0) * 0.1, vUv.y + sin(vUv.x * 30.0) * 0.1);float strength = 1.0 - step(0.01, abs(distance(wavedUv, vec2(0.5)) - 0.25));
Pattern 40
将 pattern 38 的变化周期增大。
vec2 wavedUv = vec2(vUv.x + sin(vUv.y * 100.0) * 0.1, vUv.y + sin(vUv.x * 100.0) * 0.1);float strength = 1.0 - step(0.01, abs(distance(wavedUv, vec2(0.5)) - 0.25));
Pattern 41
uv坐标取反正切atan(),f(x)=tan^(-1)(x)的取值范围是[-π, π],uv坐标没有负值取值为[0, π]
float angle = atan(vUv.x, vUv.y);float strength = angle;
Pattern 42
pattern 41 的uv坐标置于中心 -0.5
float angle = atan(vUv.x - 0.5, vUv.y - 0.5 );float strength = angle;
Pattern 43
由(0.5, 0.5)开始的角度渐变纹理
float angle = atan(vUv.x - 0.5, vUv.y - 0.5);angle /= PI * 2.0; // 映射到 [0, 1]angle += 0.5;float strength = angle;
Pattern 44
pattern 43 取模分段
float angle = atan(vUv.x - 0.5, vUv.y - 0.5);angle /= PI * 2.0;angle += 0.5;angle *= 20.0;angle = mod(angle, 1.0);float strength = angle;
Pattern 45
pattern 40 和 Pattern 43 组合
float angle = atan(vUv.x - 0.5, vUv.y - 0.5);angle /= PI * 2.0;angle += 0.5;float sinusoid = sin(angle * 100.0);float radius = 0.25 + sinusoid * 0.01;// 计算到中心点(0.5, 0.5)的距离,sqrt((x-0.5)^2 + (y-0.5)^2)float strength = 1.0 - step(0.01, abs(distance(vUv, vec2(0.5)) - radius));
Pattern 46 perlin noise
perlin noise是自然噪声生成算法,具有在函数上的连续性,并可在多次调用时给出一致的数值。用来生成噪声图样,生成随机性、云朵,水面的倒影等等。perlin noise
下面是其中一种算法, Classic Perlin 2D Noise
// Classic Perlin 2D Noise// by Stefan Gustavson (https://github.com/stegu/webgl-noise)//vec2 fade(vec2 t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);}vec4 permute(vec4 x) { return mod(((x * 34.0) + 1.0) * x, 289.0);}float cnoise(vec2 P) { vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0); vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0); Pi = mod(Pi, 289.0); // To avoid truncation effects in permutation vec4 ix = Pi.xzxz; vec4 iy = Pi.yyww; vec4 fx = Pf.xzxz; vec4 fy = Pf.yyww; vec4 i = permute(permute(ix) + iy); vec4 gx = 2.0 * fract(i * 0.0243902439) - 1.0; // 1/41 = 0.024... vec4 gy = abs(gx) - 0.5; vec4 tx = floor(gx + 0.5); gx = gx - tx; vec2 g00 = vec2(gx.x, gy.x); vec2 g10 = vec2(gx.y, gy.y); vec2 g01 = vec2(gx.z, gy.z); vec2 g11 = vec2(gx.w, gy.w); vec4 norm = 1.79284291400159 - 0.85373472095314 * vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)); g00 *= norm.x; g01 *= norm.y; g10 *= norm.z; g11 *= norm.w; float n00 = dot(g00, vec2(fx.x, fy.x)); float n10 = dot(g10, vec2(fx.y, fy.y)); float n01 = dot(g01, vec2(fx.z, fy.z)); float n11 = dot(g11, vec2(fx.w, fy.w)); vec2 fade_xy = fade(Pf.xy); vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x); float n_xy = mix(n_x.x, n_x.y, fade_xy.y); return 2.3 * n_xy;}main()使用
float strength = cnoise(vUv * 10.0);
Pattern 47
cnoise()返回的是正数和负数,step()取值
float strength = step(0.0, cnoise(vUv * 10.0));
Pattern 48
ads()
float strength = 1.0 - abs(cnoise(vUv * 10.0));
pattern 49
线条变化
float strength = sin(cnoise(vUv * 10.0) * 20.0);
pattern 50
pattern 49 取 step()
float strength = step(0.9, sin(cnoise(vUv * 10.0) * 20.0));
Mix Color
混合颜色,通过mix()混合颜色
float strength = step(0.9, sin(cnoise(vUv * 10.0) * 20.0));
vec3 blackColor = vec3(0.0); // blackvec3 uvColor = vec3(vUv, 1.0);vec3 mixedColor = mix(blackColor, uvColor, strength); // 混合颜色gl_FragColor = vec4(mixedColor, 1.0);
但是在 pattern 11 中,图案叠加时strength超过了1,混合是第三个值会超过1,颜色的值被计算到更大的范围,所以需要限制界限
float strength = step(0.8, mod(vUv.x * 10.0, 1.0));strength += step(0.8, mod(vUv.y * 10.0, 1.0));
vec3 blackColor = vec3(0.0); // blackvec3 uvColor = vec3(vUv, 1.0);vec3 mixedColor = mix(blackColor, uvColor, strength); // 混合颜色gl_FragColor = vec4(mixedColor, 1.0);交点处颜色明显更亮,因为 strength > 1.0

strength = clamp(strength, 0.0, 1.0); // 限制范围在0-1之间
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














