前言
今天我们来学习 shader 中的 clamp 函数。话不多说,我们直接开始吧。
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 中的每个分量限制在指定的范围内,并返回一个新的向量,其中每个分量都被限制在 minVal 和 maxVal 指定的范围内。
例如:
vec2(1.5, 2.5),minVal 是 vec2(1.0, 2.0),而 maxVal 是 vec2(2.0, 3.0),则 clamp(x, minVal, maxVal) 的结果将是 vec2(1.5, 2.5),因为 x 的每个分量都在指定的范围内。
如果 x 的某个分量小于 minVal 对应的分量,则该分量将被限制为 minVal 对应的分量值;如果 x 的某个分量大于 maxVal 对应的分量,则该分量将被限制为 maxVal 对应的分量值。
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 实例
线段
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
//获取点p到线段ab的距离
float sdSegment(in vec2 p, in vec2 a, in vec2 b) {
vec2 pa = p - a, ba = b - a;
float k = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * k);
}
void main() {
vec2 uv = (gl_FragCoord.xy * 2. - u_resolution.xy) /
min(u_resolution.x, u_resolution.y); // uv:[-1,1]
float d1 = step(
sdSegment(uv, vec2(0.1), vec2(0.8)),
0.03); // 判断最短距离是否小于 0.03。如果小于 0.03,返回 1.0,否则返回 0.0
}
行走的表盘
我们已经能画一个线段了,我们现在利用线段来实现表盘
#define DISCREET_SECONDS //定义这个宏表示秒针的运动是离散的(每秒跳动一次)
#define AA 4. //定义抗锯齿的级别为4
#define PI 3.14159265359 //定义圆周率
#define EPS .01 //定义一个小的常数,用于控制边缘的锐化
// 距离函数——限制在[0,1]
float df_disk(in vec2 p, in vec2 c, in float r) {
return clamp(length(p - c) - r, 0., 1.);
}
// 距离函数——判断在圆内部还是外部
float df_circ(in vec2 p, in vec2 c, in float r) {
return abs(r - length(p - c));
}
//获取点到线的距离——话线段
float df_line(in vec2 p, in vec2 a, in vec2 b) {
vec2 pa = p - a, ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0., 1.);
return length(pa - ba * h);
}
//防锯齿函数
float sharpen(in float d, in float w) {
float e = 1. / min(iResolution.y, iResolution.x);
return 1. - smoothstep(-e, e, d - w);
}
// 旋转函数——p围绕圆心旋转t弧度
vec2 rotate(in vec2 p, in float t) {
t = t * 2. * PI;
return vec2(p.x * cos(t) - p.y * sin(t), p.y * cos(t) + p.x * sin(t));
}
// 场景函数
float df_scene(vec2 uv) {
float thrs = iDate.w / 3600.; // 时
float tmin = mod(iDate.w, 3600.) / 60.; //分
float tsec = mod(mod(iDate.w, 3600.), 60.); //秒
// 如果这个宏定义存在 —— 秒向下取整
#ifdef DISCREET_SECONDS
tsec = floor(tsec);
#endif
vec2 c = vec2(0), u = vec2(0, 1);
float c1 = sharpen(df_circ(uv, c, .90), EPS * 1.5); //外圆
float c2 = sharpen(df_circ(uv, c, .04), EPS * 0.5); //内圆
float d1 = sharpen(df_disk(uv, c, .01), EPS * 1.5);
float l1 =
sharpen(df_line(uv, c, rotate(u, -thrs / 12.) * .60), EPS * 1.7); //时
float l2 =
sharpen(df_line(uv, c, rotate(u, -tmin / 60.) * .80), EPS * 1.0); //分
float l3 =
sharpen(df_line(uv, c, rotate(u, -tsec / 60.) * .85), EPS * 0.5); //秒
return max(max(max(max(max(l1, l2), l3), c1), c2), d1); //返回整个表盘
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = (fragCoord.xy / iResolution.xy * 2. - 1.);
uv.x *= iResolution.x / iResolution.y;
vec3 col = vec3(0);
#ifdef AA
// Antialiasing via supersampling
float e = 1. / min(iResolution.y, iResolution.x);
for (float i = -AA; i < AA; ++i) {
for (float j = -AA; j < AA; ++j) {
col += df_scene(uv + vec2(i, j) * (e / AA)) / (4. * AA * AA);
}
}
#else
col += df_scene(uv);
#endif /* AA */
fragColor = vec4(col, 1);
}
总结
以上便是 shader——clamp 函数的全部内容了。感兴趣的小伙伴可以自己试下这些效果。如有错误之处,欢迎大家留言指出。谢谢大家了。