简介
shader代码来自github上一个项目,compose libraries,但是链接找不到了。。。,代码很简洁,但是里面的技巧适合我这种初学者领会。
Shader代码
const float PI = 3.14159265359;
// 按角度旋转。(安卓的坐标由于从左上开始,所以旋转角度与正常坐标系相反)
mat2 rot(float a) {
float s = sin(a), c = cos(a);
return mat2(c, -s, s, c);
}
// 矩形区域
float sdBox(vec2 p, vec2 b) {
vec2 d = abs(p)-b;
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
// 光柱数量
const float MAX_STEPS = 24.0;
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
// 坐标系变换至-1到1
vec2 uv = fragCoord / iResolution.xy * 2.0 - 1.0; // -1..0..1 coordinate system
float aspect = iResolution.y / iResolution.x; // aspect ratio
if (aspect > 1.0) { // landscape mode?
uv.y *= aspect;
} else {
uv.x /= aspect;
}
// 随时间变化的角度
float angle = -PI * 2.0 * iTime;
float l = 0.0; // luminance
for (float i = .0; i < MAX_STEPS; i++) {
// 光柱角度
float rectAngle = PI * 2.0 / MAX_STEPS * i;
// 旋转
vec2 p = uv * rot(rectAngle); // rotate
// 位移
p.x += 0.4; // move outside
// 与当前角度的差
float angleDistance = mod(rectAngle - angle, 2.0 * PI);
// 随当前角度距离越远,强度越低
float intensity = 1.0 - (angleDistance / (PI * 2.0));
// 矩形区域,长度随强度变化
float box = sdBox(p, vec2(0.2 * (intensity + .7), 0.003));
// 矩形随强度平滑缩小
box = smoothstep(0.0, max(0.03, 0.13 * intensity) , box);
//l = max(l, 1.0 - box);
// 绘制矩形区域
l += 1.0 - box;
}
// 校正至0 1区间。(shadertoy网页版clamp的实现有些问题,返回的都是1.;暂不清楚原因)
l = clamp(0., 1., l);
vec3 col = vec3(.1, 1., 0.) * l;
//col = mix(vec3(1.), col, l);
fragColor = vec4(col, l);
}
代码很少,但是效果还不错,个人觉得这里值得初学者领会的有几点:
1 旋转。涉及到二维变换矩阵。
2 旋转后绘制。与安卓Canvas画图时旋转画布的原理类似,不同的是这里是uv,对我个人来讲,直接面对uv,理解上总是会绕。
3 偏移。p.x += 0.4; 非常实用的小技巧,可以让光柱偏离圆心,视觉效果上,由于长度跟随intensity(角度)变化,看上去就是一个缓慢变化的位移。
4 循环,绘制区域的计算。l += 1.0 - box; 这里用到+号,但是由于输入是uv,而uv实际上只会存在于某个光柱区域,所以实际上累加到其它区域时加的是0,而循环的效果其实就是计算当前帧所有光柱区域。
总结
前面在写shader的时候,习惯于算相对位置和区域,对比这个例子,旋转、位移、循环,用的都很经典,值得好好领会。(我觉得领会这个词对我学习shader很适用,就是很多场景,你觉得你理解了,但其实并没有。。。)