• 作者:老汪软件技巧
  • 发表时间:2024-08-22 00:03
  • 浏览量:

通过几个月的 Shader学习,看到一些酷炫大屏效果时,感觉我上我也行。本文将列举一些实现/学习的小效果, 当然这种用 shader直接渲染的方法一定是计算性能相对较大的,但是在编写过程中发现其实时渲染带来的视觉实时可编程可操作会让产品 UI带来更强的交互能力。

本文的 Shader都在

扫描雷达

扫描雷达主要是有两部分逻辑组成, 第一部分通过 Segment SDF函数构建一条白线绕着心旋转,第二部分是利用白线的角度差做一个亮度递减的残影,用来模拟光运动的视觉停留。SDF函数可以参考之前写过的这篇

float radar(vec2 uv, vec2 center, float radius) {
    // steadily increasing angles for moving
    float theta = fract(iTime * .25) * 2. * PI;
    vec2 p = uv - center;
    float r = length(p);
	   
    if(r// p1 moving along the circumference of circle
        vec2 p1 = radius* vec2(cos(theta), sin(-theta)) + center;
        // sign distancce function
        float l = sdSegment(uv, p0, p1);
		// angle from 0 to 2*PI
        float angle = mod(atan( p.y , p.x) + theta, 2. *PI)  ;
		// light range
        float range = 0.5 * PI;
        // the closer to the line, the stronger the color.
        float intensity = clamp (range -  angle, 0.0, range) / range;
       
        return SMOOTH(l*200., 1.) + 0.8*intensity;
   
   
    }
    else return 0.0;
}

SMOOTH是一个宏,这个函数中 200 调整越大,宽度越小

#define SMOOTH(r,R) (1.0-smoothstep(R-1.0,R+1.0, r))

告警

这个也是有两部分组成,一个是往外扩散的圆,另外是一个不断闪烁的圆。 都是通过 圆的 SDF实现。只是结合了一些时间和半径的变化达到运动的效果。关于圆的 SDF可以查看这篇,也是 Shader学习之旅的第一篇

#define SMOOTH2(r,R) (1.0-smoothstep(R-0.01, R+0.01, r))
float flashingDot(vec2 uv, vec2 center, float radius, float frequency) {
    float dist = sdCircle(uv-center, radius);
    // from 0 to 1
    float waveFunc = 0.5-0.5*cos(iTime * 2.0 * PI * frequency);
    return waveFunc * SMOOTH2(dist,radius);
}

_数字孪生mbse_数字孪生3d展示

比较有意思的是这个波函数0.5-0.5*cos(iTime * 2.0 * PI * frequency)

扩展的圆有一下两种方法,第一种利用 三角函数制作出波,并用 exp函数制作出光。

float waveCircle(vec2 uv, vec2 center, float waveCount, float speed) {
    float dist = length(uv - center);
    
    float wave = sin(dist*PI*waveCount - iTime*speed);
    return exp(wave*wave * -3. );
}

或者明确最小半径和最大半径,通过 smoothstep 制作出光的效果

float waveCirlce2(vec2 uv, vec2 center, float startR, float endR, float speed) {
    float dist = length(uv - center);
    float waveR = startR + mod(iTime*speed, endR-startR);
    return smoothstep(max(0.09,waveR-0.11),waveR,dist)-SMOOTH2(waveR,dist);
    
}

直线Dash

在表示运动的时,常常是将一个线变成 dash, 通过 dash的移动表示线的移动。线的 dash可以表述为距离线起点的 50% 显示,另外 50%不显示。 对应 dash的数量,可以通过mod来实现.

float dashRect(vec2 uv, vec2 center, vec2 wh, float dashed, float speed, float width) {
    vec2 dist = abs(uv - center);
    // draw rectangle
    if (all(lessThan(dist, wh)) && any(greaterThan(dist, wh-width))) {
        
        vec2 pixel = uv;
        float aspect = wh.y / wh.x;
        // Positive if on the left side of a 45-degree line, negative otherwise
        float dir = (dist.x * aspect > dist.y) ?
        -sign(uv.x - center.x) : sign(uv.y - center.y);
        float dash = step(dashed/2.0, mod(dir*(pixel.x + pixel.y) + iTime * speed, dashed));
        return dash;
    }
    return 0.0;
}

一个需要注意的点, 是运动的方向。 在上边和左边是朝着 x见小/y减小的方向移动。 其他是朝着增大方向移动。 要根据这一个特性进行方向调整, 以上的代码是逻辑优化后的结果。

圆Dash

对于圆来说,dash其实就是按照角度对于圆的边缘显示与否。 同样也是分为两部,第一部通过 smooth画出圆的边缘, 另外通过 mod 控制角度和 gap。 最后的 intensity是为了区分不同的 dash的亮度,这样在旋转的时候不止可以通过dash的移动判断旋转方向,也可以通过较亮的dash判断方向。

float dashCircle(vec2 uv, vec2 center, float radius, float width, float gapAngle, float gapCount)
{
    vec2 pixel = uv - center;
    float dist = length(pixel);
    float r = dist;
    float theta = 180.0*(atan(pixel.y,pixel.x)/PI);
    float baseAngle = 180. / gapCount;
    
    float outerCircle = SMOOTH2(dist-width/2.0,radius);
    float innerCircle = SMOOTH2(dist+width/2.0,radius);
    
    float gap = smoothstep(gapAngle, gapAngle+0.1, abs(mod(theta+gapAngle, baseAngle)-gapAngle));
    float intensity =  mix( 0.2, 1.0, step(0., mod(theta, 90.0)-45.0) ) ;
    return gap *  intensity * (outerCircle - innerCircle);
}