Learn ComputeShader 10 HUD Overlay

前言:

1. HUD Overlay (Head-Up Display Overlay)

  • 定义: HUD 是指游戏或应用程序中的一类叠加界面元素,通常显示在屏幕上,用于向用户提供实时信息。它通常显示关键信息而不会打断用户的主要活动或视线。
  • 应用场景: 常见于游戏、飞行模拟器和驾驶游戏中,HUD 元素包括生命值、弹药数、速度、地图、目标标记等。
  • 特点: HUD 通常位于屏幕的上层,直接显示在用户的视野中,不与用户的输入有直接交互,目的是提供信息的可视化呈现。

2. UI (User Interface)

  • 定义: UI 是一个更广泛的概念,指的是用户和系统之间进行交互的界面。UI 包括所有用户可见的界面元素,例如按钮、菜单、滑块、对话框等。
  • 应用场景: UI 包括了游戏菜单、设置界面、任务面板等,适用于任何需要用户与系统进行交互的场景。
  • 特点: UI 通常涉及到用户的操作,例如点击、拖动或输入,用户可以通过这些元素直接与系统进行交互。

区别

  • HUD Overlay 是 UI 的一部分: HUD 是 UI 的一个子集,但专注于向用户提供信息,而不是与用户交互。例如,HUD 可以显示游戏中的玩家生命值、当前任务状态等,但它一般不会包含需要用户直接操作的元素。
  • 交互性: HUD 通常是静态的或半动态的显示信息,不要求用户主动操作,而 UI 元素通常需要用户输入或点击,提供更多的互动性。

类比:

  • HUD 是 UI 中的一种展示方式,像汽车仪表盘上的速度表一样,持续显示信息。
  • UI 则像车载导航系统中的触摸屏菜单,用户可以通过点击进行交互和控制。

下面要实现一个下图这样的hudoverlay效果

首先生成水平和垂直两条竖线以及周围的三层圆圈。

为了生成的水平和垂直的线都在屏幕中央,我们有必要进行坐标转换。根据原图像的宽高比对像素坐标进行缩放,同时调整屏幕中心点的位置。

之后我们就可以通过调整后的坐标在指定 轴上生成一条线。用的就是下面这个函数,这个函数主要就是检测x和y的接近程度(程度取决于后两个参数),只要接近就会返回1,否则返回0

float onLine(float x, float y, float line_width, float edge_width){
    return smoothstep(x-line_width/2.0-edge_width, x-line_width/2.0, y) - smoothstep(x+line_width/2.0, x+line_width/2.0+edge_width, y);
}

通过这个函数添加水平和垂直两个轴

[numthreads(8, 8, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    float3 white = 1;
    
    float2 uv = (float2)id.xy;
    float2 center = 0.5;
    float aspectRatio = (float)source.Length.x/(float)source.Length.y;

    if (aspectRatio>1){
        uv /= (float)source.Length.y;
        center.x *= aspectRatio; 
    }else{
        uv /= (float)source.Length.x;
        center.y /= aspectRatio;
    }

    float3 color = onLine(uv.y, center.y, 0.002, 0.001) * axisColor.rgb;//xAxis
    color+= onLine(uv.x, center.x, 0.002, 0.001) * axisColor.rgb;//xAxis
    //TO DO: Add code here

    float alpha = saturate(color.r + color.g + color.b);
    float3 finalColor = lerp(source[id.xy].rgb, color, alpha);

    output[id.xy] = float4(finalColor, 1.0);
}

效果:

 然后添加周围的三个圆圈

下面这个函数可以生成一个圆形

float circle(float2 pt, float2 center, float radius, float line_width, float edge_thickness){
    pt -= center;
    float len = length(pt);
    //Change true to false to soften the edge
    float result = smoothstep(radius-line_width/2.0-edge_thickness, radius-line_width/2.0, len) - smoothstep(radius + line_width/2.0, radius + line_width/2.0 + edge_thickness, len);

    return result;
}

 生成三个圆形

    color+= circle(uv, center, 0.45,0.002, 0.001) * axisColor.rgb;
    color+= circle(uv, center, 0.30,0.002, 0.001) * axisColor.rgb;
    color+= circle(uv, center, 0.15,0.002, 0.001) * axisColor.rgb;

效果:

目前为止我们已经完成了所有静态效果,下面完成动态效果 。

首先生成一个动态的绕着圆弧旋转的直线,同时在直线后面增加一个尾随的渐变效果。可以利用下面的函数。(数学原理暂时没搞懂)

float sweep(float2 pt, float2 center, float radius, float line_width, float edge_thickness){
    float2 d = pt - center;
    float theta = time;
    float2 p = float2(cos(theta), -sin(theta))*radius;
    float h = clamp( dot(d,p)/dot(p,p), 0.0, 1.0 );
    //h = dot(d,p)/dot(p,p);
    float l = length(d - p*h);

    float gradient = 0;
    const float gradient_angle = PI * 0.5;
    if (length(d) < radius) {
        float angle = fmod(theta + atan2(d.y, d.x), PI * 2.0);
        gradient = clamp(gradient_angle - angle, 0.0, gradient_angle) / gradient_angle * 0.5;
    }

    return gradient+1.0 - smoothstep(line_width, line_width+edge_thickness, l);
}

最后就是生成水平轴两边的小三角形,可以利用下面的函数

float polygon(float2 pt, float2 center, float radius, int sides, float rotate, float edge_thickness){
    pt -= center;

    // Angle and radius from the current pixel 
    float theta = atan2(pt.y, pt.x) + rotate;
    float rad = PI2/float(sides);

    // Shaping function that modulate the distance    
    float d = cos(floor(0.5 + theta/rad)*rad-theta)*length(pt);

    return 1.0 - smoothstep(radius, radius + edge_thickness, d);
}

最后是完整的核函数代码:

[numthreads(8, 8, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    float3 white = 1;
    
    float2 uv = (float2)id.xy;
    float2 center = 0.5;
    float aspectRatio = (float)source.Length.x/(float)source.Length.y;

    if (aspectRatio>1){
        uv /= (float)source.Length.y;
        center.x *= aspectRatio; 
    }else{
        uv /= (float)source.Length.x;
        center.y /= aspectRatio;
    }

    float3 color = onLine(uv.y, center.y, 0.002, 0.001) * axisColor.rgb;//xAxis
    color+= onLine(uv.x, center.x, 0.002, 0.001) * axisColor.rgb;//yAxis
    color+= circle(uv, center, 0.45,0.002, 0.001) * axisColor.rgb;
    color+= circle(uv, center, 0.30,0.002, 0.001) * axisColor.rgb;
    color+= circle(uv, center, 0.15,0.002, 0.001) * axisColor.rgb;

    color+= sweep(uv, center, 0.45,0.002, 0.001) * sweepColor.rgb;

    float offset = sin(time * 4) * 0.05 + 0.5;
    color += polygon(uv, float2(center.x + offset, center.y), 0.008, 3, 0.0, 0.001) * white;
    color += polygon(uv, float2(center.x - offset, center.y), 0.008, 3, PI, 0.001) * white;

    float alpha = saturate(color.r + color.g + color.b);
    float3 finalColor = lerp(source[id.xy].rgb, color, alpha);

    output[id.xy] = float4(finalColor, 1.0);
}

最终效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值