Unity中如何绘制屏幕空间下宽度恒定的线条?
Unity实现屏幕宽度恒定的线条方案
方案一:自定义Shader适配LineRenderer
这是最接近KSP轨道线效果的方案,通过Shader将线条宽度转换为屏幕空间单位,彻底抵消透视投影的缩放影响。
Shader代码示例
Shader "Custom/ScreenSpaceLine" { Properties { _LineColor ("Line Color", Color) = (1,1,1,1) _LineWidth ("Line Width (Pixels)", Float) = 2.0 } SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" } LOD 100 Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float4 screenPos : TEXCOORD1; }; fixed4 _LineColor; float _LineWidth; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.screenPos = ComputeScreenPos(o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // 计算屏幕空间的宽度因子,抵消透视缩放 float widthFactor = _LineWidth / _ScreenParams.y; float2 screenUV = i.screenPos.xy / i.screenPos.w; float2 uv = i.uv; // 计算线条边缘的抗锯齿 float edge = smoothstep(0.5 - widthFactor, 0.5, uv.y) - smoothstep(0.5, 0.5 + widthFactor, uv.y); fixed4 col = _LineColor * edge; return col; } ENDCG } } }
使用方法
- 创建上述Shader并赋予LineRenderer的材质
- 在材质面板调整
Line Width (Pixels)参数,设置你想要的固定屏幕像素宽度 - LineRenderer的
Width属性可以设为1(Shader会忽略其世界空间宽度,改用屏幕像素宽度)
方案二:动态计算世界空间宽度(无需自定义Shader)
如果不想编写Shader,可以通过脚本实时计算每个线段的世界空间宽度,抵消透视带来的缩放。核心逻辑是:根据线段到相机的距离,计算对应的世界空间宽度,保证屏幕上的宽度一致。
C#代码示例
using UnityEngine; [RequireComponent(typeof(LineRenderer))] public class ScreenSpaceLineWidth : MonoBehaviour { [Tooltip("屏幕上的固定宽度(像素)")] public float screenWidth = 2f; private LineRenderer _lineRenderer; private Camera _mainCamera; void Start() { _lineRenderer = GetComponent<LineRenderer>(); _mainCamera = Camera.main; UpdateLineWidths(); } void Update() { UpdateLineWidths(); } void UpdateLineWidths() { int pointCount = _lineRenderer.positionCount; if (pointCount < 2) return; float[] widths = new float[pointCount]; for (int i = 0; i < pointCount; i++) { Vector3 worldPos = _lineRenderer.GetPosition(i); // 计算点到相机的距离 float distance = Vector3.Distance(_mainCamera.transform.position, worldPos); // 转换为世界空间宽度:屏幕像素宽度 -> 视场角对应的世界单位 float worldWidth = screenWidth * distance / (_mainCamera.pixelHeight * Mathf.Tan(_mainCamera.fieldOfView * 0.5f * Mathf.Deg2Rad)); widths[i] = worldWidth; } _lineRenderer.SetWidths(widths); } }
注意事项
- 此方案需要在Update中实时更新,若线条点较多可能有性能开销
- 当相机FOV或分辨率变化时,脚本会自动适配
内容的提问来源于stack exchange,提问作者kharsair
相关产品推荐
相关产品推荐

