前言:
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);
}
最终效果: