经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 软件/图像 » unity » 查看文章
【Unity3D】激光雷达特效
来源:cnblogs  作者:little_fat_sheep  时间:2023/8/14 8:52:32  对本文有异议

1 由深度纹理重构世界坐标

? 屏幕深度和法线纹理简介中对深度和法线纹理的来源、使用及推导过程进行了讲解,本文将介绍使用深度纹理重构世界坐标的方法,并使用重构后的世界坐标模拟激光雷达特效。

? 本文完整资源见→Unity3D激光雷达特效

? 1)重构像素点世界坐标

? 对于屏幕上的任意一点,它对应的世界坐标系中的点记为 P,对应的*裁剪*面上的点记为 Q,相机位置记为 O(坐标为 _WorldSpaceCameraPos),假设 P 点的深度为 depth(由 LinearEyeDepth 函数获取),相机到**面的距离为 near,如下图所示。

img

? 根据上图,可以列出以下方程组关系。其中,公式 2 由三角形相似原理得到,公式 3 由 O、P、Q 三点共线得到。

img

? 化简得:

img

? Q 点在**面上,可以通过*裁剪*面的四个角插值得到,O 和 near 为定值,因此 (OQ / near) 也可以通过插值得到。假设*裁剪*面的四个角分别为 A、B、C、D,我们将 (OA / near)、(OB / near)、(OC / near)、(OD / near) 输入顶点着色器中,光珊化会自动为我们计算插值后的 (OQ / near)。

? 如下,我们可以在插值寄存器中定义变量 interpolatedRay,用于存储向量 (OQ / near)。

  1. struct v2f {
  2. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
  3. half2 uv : TEXCOORD0; // 纹理uv坐标
  4. float4 interpolatedRay : TEXCOORD1; // 插值射线向量(由相机指向**面上点的向量除以near后的坐标)
  5. };

? 2)*裁剪*面四角射线向量计算

? 记*裁剪*面上左下角、右下角、右上角、左上角、中心、右中心、上中心顶点分别为 A、B、C、D、Q、E、F,相机位置为 O 点,如下:

img

? 根据几何关系,可以计算向量 OA、OB、OC、OD 如下:

img

? 假设摄像机竖直方向的视野角度为 fov(通过 camera.fieldOfView 获取),屏幕宽高比为 aspect(通过 camera.aspect 获取),相机距离*裁剪*面的距离为 near(通过 camera.nearClipPlane 获取),相机向右、向上、向前方向的单位方向向量分别为 right、up、forward(通过 camera.transform 组件获取),则向量 OQ、QE、QF 的计算如下:

img

2 间距均匀的雷达波特效

2.1 雷达波扩散原理

? 对于屏幕上任意一点,假设其对应的世界坐标为 worldPos,其线性深度值为 lineDepth(通过 LinearEyeDepth 函数获取),如果 lineDepth >= far - 1(far 通过 _ProjectionParams.z 获取),说明该点落在天空中,不参与雷达波计算,因此本文仅考虑 lineDepth < far - 1 的像素点雷达波计算。

? 假设雷达波中心坐标为 waveCenter,波纹间距为 waveGap,波纹宽度为 waveLineWidth,雷达波的传播速度和传播时间分别为 waveSpeed、waveTime,雷达波的发射周期为 waveCastTime,雷达波发射的初始距离为 initWaveDist,当前顶点被采样为目标纹理颜色的比率因子为 factor,波纹颜色为 waveColor,当前顶点在叠加雷达波前后的颜色分别为 tex、finalColor,则 finalColor 的计算如下:

  1. float len = length(worldPos - waveCenter); // 当前顶点距离雷达波中心的距离
  2. float time = fmod(waveTime, waveCastTime); // 当前发射周期中, 雷达波传播的时间
  3. float dist = initWaveDist + waveSpeed * time; // 当前发射周期中, 雷达波传播的距离
  4. float mod = fmod(abs(dist - len), waveGap); // 当前顶点距离最*的内环波纹的距离
  5. float rate = min(min(mod, waveGap - mod), waveLineWidth) / waveLineWidth; // 当前顶点处在波纹范围外的比率(值域: [0,1])
  6. float factor = smoothstep(0, 1, rate); // 当前顶点被采样为目标纹理颜色的比率因子(值域: [0,1])
  7. fixed4 finalColor = lerp(waveColor, tex, factor); // 当前顶点叠加雷达波后的颜色

2.2 点选设置雷达波中心

? LaserRadar.cs

  1. using UnityEngine;
  2. [RequireComponent(typeof(Camera))] // 需要相机组件
  3. public class LaserRadar : MonoBehaviour {
  4. public Color waveColor = Color.red; // 雷达波的颜色
  5. [Range(0.1f, 0.49f)]
  6. public float waveLineWidth = 0.49f; // 雷达波纹的宽度
  7. [Range(1, 10)]
  8. public float waveGap = 2; // 雷达波的间距
  9. [Range(0.5f, 10f)]
  10. public float waveSpeed = 1f; // 雷达波传播的速度
  11. [Range(3, 10)]
  12. public float waveCastTime = 10; // 雷达波发射的时间周期
  13. [Range(3, 10)]
  14. public int waveNum = 5; // 每个发射周期的波纹数
  15. [Range(0.1f, 20)]
  16. public float initWaveDist = 3; // 雷达波初始的距离
  17. [Range(10, 200)]
  18. public float maxWaveDist = 30f; // 雷达波传播的最远距离
  19. private bool enableWave = false; // 是否开启雷达波特效
  20. private Vector4 waveCenter; // 雷达波中心
  21. private float waveTime = 0; // 雷达波开始时间
  22. private Camera cam; // 相机
  23. private Material material = null; // 材质
  24. private void Awake() {
  25. cam = GetComponent<Camera>();
  26. material = new Material(Shader.Find("MyShader/LaserRadar"));
  27. material.hideFlags = HideFlags.DontSave;
  28. }
  29. private void OnEnable() {
  30. cam.depthTextureMode |= DepthTextureMode.Depth;
  31. }
  32. private void Update() {
  33. if (Input.GetMouseButtonDown(0)) {
  34. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  35. RaycastHit hitInfo;
  36. if (Physics.Raycast(ray, out hitInfo)) {
  37. enableWave = true;
  38. material.SetInt("_Enable", 1);
  39. waveCenter = hitInfo.point;
  40. material.SetVector("_WaveCenter", waveCenter);
  41. waveTime = 0;
  42. }
  43. }
  44. if (enableWave) {
  45. waveTime += Time.deltaTime;
  46. if (waveTime > waveCastTime) {
  47. enableWave = false;
  48. material.SetInt("_Enable", 0);
  49. }
  50. }
  51. }
  52. private void OnRenderImage(RenderTexture src, RenderTexture dest) {
  53. if (enableWave) {
  54. Matrix4x4 frustumCorners = GetFrustumCornersRay();
  55. material.SetMatrix("_FrustumCornersRay", frustumCorners);
  56. material.SetColor("_WaveColor", waveColor);
  57. material.SetFloat("_WaveLineWidth", waveLineWidth);
  58. material.SetFloat("_WaveGap", waveGap);
  59. material.SetFloat("_WaveSpeed", waveSpeed);
  60. material.SetFloat("_WaveTime", waveTime);
  61. material.SetFloat("_WaveCastTime", waveCastTime);
  62. material.SetFloat("_WaveNum", waveNum);
  63. material.SetFloat("_InitWaveDist", initWaveDist);
  64. material.SetFloat("_MaxWaveDist", maxWaveDist);
  65. Graphics.Blit(src, dest, material);
  66. } else {
  67. Graphics.Blit(src, dest);
  68. }
  69. }
  70. private Matrix4x4 GetFrustumCornersRay() { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  71. Matrix4x4 frustumCorners = Matrix4x4.identity;
  72. float fov = cam.fieldOfView;
  73. float near = cam.nearClipPlane;
  74. float aspect = cam.aspect;
  75. float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
  76. Vector3 toRight = cam.transform.right * halfHeight * aspect; // 指向右方的向量
  77. Vector3 toTop = cam.transform.up * halfHeight; // 指向上方的向量
  78. Vector3 toForward = cam.transform.forward * near; // 指向前方的向量
  79. Vector3 bottomLeft = (toForward - toTop - toRight) / near; // 指向左下角的射线
  80. Vector3 bottomRight = (toForward + toRight - toTop) / near; // 指向右下角的射线
  81. Vector3 topRight = (toForward + toRight + toTop) / near; // 指向右上角的射线
  82. Vector3 topLeft = (toForward + toTop - toRight) / near; // 指向左上角的射线
  83. frustumCorners.SetRow(0, bottomLeft);
  84. frustumCorners.SetRow(1, bottomRight);
  85. frustumCorners.SetRow(2, topRight);
  86. frustumCorners.SetRow(3, topLeft);
  87. return frustumCorners;
  88. }
  89. }

? LaserRadar.shader

  1. Shader "MyShader/LaserRadar" { // 雷达波特效
  2. Properties{
  3. _MainTex("Base (RGB)", 2D) = "white" {} // 主纹理
  4. _Enable("Enable", Int) = 0 // 是否启动雷达波特效
  5. _WaveColor("WaveColor", Color) = (1, 0, 0, 1) // 雷达波的颜色
  6. _WaveLineWidth("WaveLineWidth", Float) = 0.49 // 雷达波纹条的宽度
  7. _WaveCenter("WaveCenter", Vector) = (0, 0, 0, 0) // 雷达的波中心
  8. _WaveGap("WaveGap", Float) = 2 // 雷达波的间距
  9. _WaveSpeed("WaveSpeed", Float) = 1 // 雷达波的传播速度
  10. _WaveTime("WaveTime", Float) = 0 // 雷达波传播的时间
  11. _WaveCastTime("WaveCastTime", Float) = 10 // 雷达波发射的时间周期
  12. _WaveNum("WaveNum", Int) = 5 // 每个发射周期的波纹数
  13. _InitWaveDist("InitWaveDist", Float) = 3 // 雷达波初始的距离
  14. _MaxWaveDist("MaxWaveDist", Float) = 30 // 雷达波传播的最远距离
  15. }
  16. SubShader{
  17. Pass {
  18. // 深度测试始终通过, 关闭深度写入
  19. ZTest Always ZWrite Off
  20. CGPROGRAM
  21. #include "UnityCG.cginc"
  22. #pragma vertex vert
  23. #pragma fragment frag
  24. sampler2D _MainTex; // 主纹理
  25. sampler2D _CameraDepthTexture; // 深度纹理
  26. float4x4 _FrustumCornersRay; // 视锥体四角射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  27. int _Enable; // 是否启动雷达波特效
  28. fixed4 _WaveColor; // 雷达波的颜色
  29. float _WaveLineWidth; // 雷达波纹的宽度
  30. float4 _WaveCenter; // 雷达波的中心
  31. float _WaveGap; // 雷达波的间距
  32. float _WaveSpeed; // 雷达波的速度
  33. float _WaveTime; // 雷达波传播的时间
  34. float _WaveCastTime; // 雷达波发射的时间周期
  35. int _WaveNum; // 每个发射周期的波纹数
  36. float _InitWaveDist; // 雷达波初始的距离
  37. float _MaxWaveDist; // 雷达波传播的最远距离
  38. struct v2f {
  39. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
  40. half2 uv : TEXCOORD0; // 纹理uv坐标
  41. float4 interpolatedRay : TEXCOORD1; // 插值射线向量(由相机指向**面上点的向量除以near后的坐标)
  42. };
  43. float4 getInterpolatedRay(half2 uv) { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  44. int index = 0;
  45. if (uv.x < 0.5 && uv.y < 0.5) {
  46. index = 0;
  47. } else if (uv.x > 0.5 && uv.y < 0.5) {
  48. index = 1;
  49. } else if (uv.x > 0.5 && uv.y > 0.5) {
  50. index = 2;
  51. } else {
  52. index = 3;
  53. }
  54. return _FrustumCornersRay[index];
  55. }
  56. v2f vert(appdata_img v) {
  57. v2f o;
  58. o.pos = UnityObjectToClipPos(v.vertex); // 计算裁剪坐标系中顶点坐标, 等价于: mul(unity_MatrixMVP, v.vertex)
  59. o.uv = v.texcoord;
  60. o.interpolatedRay = getInterpolatedRay(v.texcoord); // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  61. return o;
  62. }
  63. fixed4 frag(v2f i) : SV_Target{
  64. if (_Enable == 0) {
  65. return tex2D(_MainTex, i.uv);
  66. }
  67. float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); // 非线性的深度, tex2D(_CameraDepthTexture, i.uv).r
  68. float linearDepth = LinearEyeDepth(depth); // 线性的深度
  69. float factor = 1;
  70. if (linearDepth < _ProjectionParams.z - 1) { // _ProjectionParams = (1, near, far, 1 / far)
  71. float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz; // 顶点世界坐标
  72. float len = length(worldPos - _WaveCenter.xyz); // 当前顶点距离雷达波中心的距离
  73. if (len < _InitWaveDist || len > _MaxWaveDist) {
  74. return tex2D(_MainTex, i.uv);
  75. }
  76. float time = fmod(_WaveTime, _WaveCastTime); // 当前发射周期中, 雷达波传播的时间
  77. float dist = _InitWaveDist + _WaveSpeed * time; // 当前发射周期中, 雷达波传播的距离
  78. if (len > dist + _WaveLineWidth || len < dist - _WaveGap * (_WaveNum - 1) - _WaveLineWidth) {
  79. return tex2D(_MainTex, i.uv);
  80. }
  81. float mod = fmod(abs(dist - len), _WaveGap); // 当前顶点距离最*的内环波纹的距离
  82. float rate = min(min(mod, _WaveGap - mod), _WaveLineWidth) / _WaveLineWidth; // 当前顶点处在波纹范围外的比率
  83. factor = smoothstep(0, 1, rate); // 当前顶点被采样为目标纹理颜色的比率因子
  84. }
  85. fixed4 tex = tex2D(_MainTex, i.uv);
  86. fixed4 color = lerp(_WaveColor, tex, factor);
  87. return color;
  88. }
  89. ENDCG
  90. }
  91. }
  92. FallBack off
  93. }

? 运行效果如下:

img

2.3 雷达波中心跟随物体运动

? LaserRadar.cs

  1. using UnityEngine;
  2. [RequireComponent(typeof(Camera))] // 需要相机组件
  3. public class LaserRadar : MonoBehaviour {
  4. public Color waveColor = Color.red; // 雷达波的颜色
  5. [Range(0.1f, 0.49f)]
  6. public float waveLineWidth = 0.49f; // 雷达波纹的宽度
  7. [Range(1, 10)]
  8. public float waveGap = 2; // 雷达波的间距
  9. [Range(0.5f, 10f)]
  10. public float waveSpeed = 1f; // 雷达波传播的速度
  11. [Range(3, 10)]
  12. public float waveCastTime = 10; // 雷达波发射的时间周期
  13. [Range(3, 10)]
  14. public int waveNum = 5; // 每个发射周期的波纹数
  15. [Range(0.1f, 20)]
  16. public float initWaveDist = 3; // 雷达波初始的距离
  17. [Range(10, 200)]
  18. public float maxWaveDist = 30f; // 雷达波传播的最远距离
  19. private bool enableWave = false; // 是否开启雷达波特效
  20. private Vector4 waveCenter; // 雷达波中心
  21. private Camera cam; // 相机
  22. private Material material = null; // 材质
  23. private Transform target; // 发射雷达波的目标物体
  24. private void Awake() {
  25. cam = GetComponent<Camera>();
  26. material = new Material(Shader.Find("MyShader/LaserRadar"));
  27. material.hideFlags = HideFlags.DontSave;
  28. target = GameObject.Find("Car").transform;
  29. }
  30. private void OnEnable() {
  31. cam.depthTextureMode |= DepthTextureMode.Depth;
  32. enableWave = true;
  33. material.SetInt("_Enable", 1);
  34. }
  35. private void OnRenderImage(RenderTexture src, RenderTexture dest) {
  36. if (enableWave) {
  37. Matrix4x4 frustumCorners = GetFrustumCornersRay();
  38. material.SetMatrix("_FrustumCornersRay", frustumCorners);
  39. material.SetColor("_WaveColor", waveColor);
  40. waveCenter = target.position;
  41. material.SetVector("_WaveCenter", waveCenter);
  42. material.SetFloat("_WaveLineWidth", waveLineWidth);
  43. material.SetFloat("_WaveGap", waveGap);
  44. material.SetFloat("_WaveSpeed", waveSpeed);
  45. material.SetFloat("_WaveCastTime", waveCastTime);
  46. material.SetFloat("_WaveNum", waveNum);
  47. material.SetFloat("_InitWaveDist", initWaveDist);
  48. material.SetFloat("_MaxWaveDist", maxWaveDist);
  49. Graphics.Blit(src, dest, material);
  50. } else {
  51. Graphics.Blit(src, dest);
  52. }
  53. }
  54. private Matrix4x4 GetFrustumCornersRay() { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  55. Matrix4x4 frustumCorners = Matrix4x4.identity;
  56. float fov = cam.fieldOfView;
  57. float near = cam.nearClipPlane;
  58. float aspect = cam.aspect;
  59. float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
  60. Vector3 toRight = cam.transform.right * halfHeight * aspect; // 指向右方的向量
  61. Vector3 toTop = cam.transform.up * halfHeight; // 指向上方的向量
  62. Vector3 toForward = cam.transform.forward * near; // 指向前方的向量
  63. Vector3 bottomLeft = (toForward - toTop - toRight) / near; // 指向左下角的射线
  64. Vector3 bottomRight = (toForward + toRight - toTop) / near; // 指向右下角的射线
  65. Vector3 topRight = (toForward + toRight + toTop) / near; // 指向右上角的射线
  66. Vector3 topLeft = (toForward + toTop - toRight) / near; // 指向左上角的射线
  67. frustumCorners.SetRow(0, bottomLeft);
  68. frustumCorners.SetRow(1, bottomRight);
  69. frustumCorners.SetRow(2, topRight);
  70. frustumCorners.SetRow(3, topLeft);
  71. return frustumCorners;
  72. }
  73. }

? LaserRadar.shader

  1. Shader "MyShader/LaserRadar" { // 雷达波特效
  2. Properties{
  3. _MainTex("Base (RGB)", 2D) = "white" {} // 主纹理
  4. _Enable("Enable", Int) = 0 // 是否启动雷达波特效
  5. _WaveColor("WaveColor", Color) = (1, 0, 0, 1) // 雷达波的颜色
  6. _WaveLineWidth("WaveLineWidth", Float) = 0.49 // 雷达波纹条的宽度
  7. _WaveCenter("WaveCenter", Vector) = (0, 0, 0, 0) // 雷达的波中心
  8. _WaveGap("WaveGap", Float) = 2 // 雷达波的间距
  9. _WaveSpeed("WaveSpeed", Float) = 1 // 雷达波的传播速度
  10. _WaveCastTime("WaveCastTime", Float) = 10 // 雷达波发射的时间周期
  11. _WaveNum("WaveNum", Int) = 5 // 每个发射周期的波纹数
  12. _InitWaveDist("InitWaveDist", Float) = 3 // 雷达波初始的距离
  13. _MaxWaveDist("MaxWaveDist", Float) = 30 // 雷达波传播的最远距离
  14. }
  15. SubShader{
  16. Pass {
  17. // 深度测试始终通过, 关闭深度写入
  18. ZTest Always ZWrite Off
  19. CGPROGRAM
  20. #include "UnityCG.cginc"
  21. #pragma vertex vert
  22. #pragma fragment frag
  23. sampler2D _MainTex; // 主纹理
  24. sampler2D _CameraDepthTexture; // 深度纹理
  25. float4x4 _FrustumCornersRay; // 视锥体四角射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  26. int _Enable; // 是否启动雷达波特效
  27. fixed4 _WaveColor; // 雷达波的颜色
  28. float _WaveLineWidth; // 雷达波纹的宽度
  29. float4 _WaveCenter; // 雷达波的中心
  30. float _WaveGap; // 雷达波的间距
  31. float _WaveSpeed; // 雷达波的速度
  32. float _WaveCastTime; // 雷达波发射的时间周期
  33. int _WaveNum; // 每个发射周期的波纹数
  34. float _InitWaveDist; // 雷达波初始的距离
  35. float _MaxWaveDist; // 雷达波传播的最远距离
  36. struct v2f {
  37. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
  38. half2 uv : TEXCOORD0; // 纹理uv坐标
  39. float4 interpolatedRay : TEXCOORD1; // 插值射线向量(由相机指向**面上点的向量除以near后的坐标)
  40. };
  41. float4 getInterpolatedRay(half2 uv) { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  42. int index = 0;
  43. if (uv.x < 0.5 && uv.y < 0.5) {
  44. index = 0;
  45. } else if (uv.x > 0.5 && uv.y < 0.5) {
  46. index = 1;
  47. } else if (uv.x > 0.5 && uv.y > 0.5) {
  48. index = 2;
  49. } else {
  50. index = 3;
  51. }
  52. return _FrustumCornersRay[index];
  53. }
  54. v2f vert(appdata_img v) {
  55. v2f o;
  56. o.pos = UnityObjectToClipPos(v.vertex); // 计算裁剪坐标系中顶点坐标, 等价于: mul(unity_MatrixMVP, v.vertex)
  57. o.uv = v.texcoord;
  58. o.interpolatedRay = getInterpolatedRay(v.texcoord); // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  59. return o;
  60. }
  61. fixed4 frag(v2f i) : SV_Target{
  62. if (_Enable == 0) {
  63. return tex2D(_MainTex, i.uv);
  64. }
  65. float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); // 非线性的深度, tex2D(_CameraDepthTexture, i.uv).r
  66. float linearDepth = LinearEyeDepth(depth); // 线性的深度
  67. float factor = 1;
  68. if (linearDepth < _ProjectionParams.z - 1) { // _ProjectionParams = (1, near, far, 1 / far)
  69. float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz; // 顶点世界坐标
  70. float len = length(worldPos - _WaveCenter.xyz); // 当前顶点距离雷达波中心的距离
  71. if (len < _InitWaveDist || len > _MaxWaveDist) {
  72. return tex2D(_MainTex, i.uv);
  73. }
  74. float time = fmod(_Time.y, _WaveCastTime); // 当前发射周期中, 雷达波传播的时间, _Time = (t/20, t, t*2, t*3)
  75. float dist = _InitWaveDist + _WaveSpeed * time; // 当前发射周期中, 雷达波传播的距离
  76. if (len > dist + _WaveLineWidth || len < dist - _WaveGap * (_WaveNum - 1) - _WaveLineWidth) {
  77. return tex2D(_MainTex, i.uv);
  78. }
  79. float mod = fmod(abs(dist - len), _WaveGap); // 当前顶点距离最*的内环波纹的距离
  80. float rate = min(min(mod, _WaveGap - mod), _WaveLineWidth) / _WaveLineWidth; // 当前顶点处在波纹范围外的比率
  81. factor = smoothstep(0, 1, rate); // 当前顶点被采样为目标纹理颜色的比率因子
  82. }
  83. fixed4 tex = tex2D(_MainTex, i.uv);
  84. fixed4 color = lerp(_WaveColor, tex, factor);
  85. return color;
  86. }
  87. ENDCG
  88. }
  89. }
  90. FallBack off
  91. }

? 运行效果如下:

img

3 间距递增的雷达波特效

3.1 雷达波扩散原理

? 对于屏幕上任意一点,假设其对应的世界坐标为 worldPos,其线性深度值为 lineDepth(通过 LinearEyeDepth 函数获取),如果 lineDepth >= far - 1(far 通过 _ProjectionParams.z 获取),说明该点落在天空中,不参与雷达波计算,因此本文仅考虑 lineDepth < far - 1 的像素点雷达波计算。

? 假设雷达波中心坐标为 waveCenter,波纹间距为 waveGap,波纹宽度为 waveLineWidth,雷达波的传播速度和传播时间分别为 waveSpeed、waveTime,雷达波的发射周期为 waveCastTime,雷达波发射的初始距离为 initWaveDist,当前顶点被采样为目标纹理颜色的比率因子为 factor,波纹颜色为 waveColor,当前顶点在叠加雷达波前后的颜色分别为 tex、finalColor,则 finalColor 的计算如下:

  1. float len = length(worldPos - waveCenter); // 当前顶点距离雷达波中心的距离
  2. float time = fmod(waveTime, waveCastTime); // 当前发射周期中, 雷达波传播的时间
  3. float waveGap = initWaveDist + waveSpeed * time; // 当前发射周期中, 雷达波传播的距离
  4. float mod = fmod(len, waveGap); // 当前顶点距离最*的内环波纹的距离
  5. float rate = min(min(mod, waveGap - mod), waveLineWidth) / waveLineWidth; // 当前顶点处在波纹范围外的比率(值域: [0,1])
  6. float factor = smoothstep(0, 1, rate); // 当前顶点被采样为目标纹理颜色的比率因子(值域: [0,1])
  7. fixed4 finalColor = lerp(waveColor, tex, factor); // 当前顶点叠加雷达波后的颜

3.2 点选设置雷达波中心

? LaserRadar.cs

  1. using UnityEngine;
  2. [RequireComponent(typeof(Camera))] // 需要相机组件
  3. public class LaserRadar1 : MonoBehaviour {
  4. public Color waveColor = Color.red; // 雷达波的颜色
  5. [Range(0.1f, 0.49f)]
  6. public float waveLineWidth = 0.49f; // 雷达波纹的宽度
  7. [Range(0.5f, 10f)]
  8. public float waveSpeed = 1f; // 雷达波传播的速度
  9. [Range(3, 10)]
  10. public float waveCastTime = 5; // 雷达波发射的时间周期
  11. [Range(0.1f, 20)]
  12. public float initWaveDist = 3; // 雷达波初始的距离
  13. [Range(10, 200)]
  14. public float maxWaveDist = 30f; // 雷达波传播的最远距离
  15. private bool enableWave = false; // 是否开启雷达波特效
  16. private Vector4 waveCenter; // 雷达波中心
  17. private float waveTime = 0; // 雷达波开始时间
  18. private Camera cam; // 相机
  19. private Material material = null; // 材质
  20. private void Awake() {
  21. cam = GetComponent<Camera>();
  22. material = new Material(Shader.Find("MyShader/LaserRadar"));
  23. material.hideFlags = HideFlags.DontSave;
  24. }
  25. private void OnEnable() {
  26. cam.depthTextureMode |= DepthTextureMode.Depth;
  27. }
  28. private void Update() {
  29. if (Input.GetMouseButtonDown(0)) {
  30. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  31. RaycastHit hitInfo;
  32. if (Physics.Raycast(ray, out hitInfo)) {
  33. enableWave = true;
  34. material.SetInt("_Enable", 1);
  35. waveCenter = hitInfo.point;
  36. material.SetVector("_WaveCenter", waveCenter);
  37. waveTime = 0;
  38. }
  39. }
  40. if (enableWave) {
  41. waveTime += Time.deltaTime;
  42. if (waveTime > waveCastTime) {
  43. enableWave = false;
  44. material.SetInt("_Enable", 0);
  45. }
  46. }
  47. }
  48. private void OnRenderImage(RenderTexture src, RenderTexture dest) {
  49. if (enableWave) {
  50. Matrix4x4 frustumCorners = GetFrustumCornersRay();
  51. material.SetMatrix("_FrustumCornersRay", frustumCorners);
  52. material.SetColor("_WaveColor", waveColor);
  53. material.SetFloat("_WaveLineWidth", waveLineWidth);
  54. material.SetFloat("_WaveSpeed", waveSpeed);
  55. material.SetFloat("_WaveTime", waveTime);
  56. material.SetFloat("_WaveCastTime", waveCastTime);
  57. material.SetFloat("_InitWaveDist", initWaveDist);
  58. material.SetFloat("_MaxWaveDist", maxWaveDist);
  59. Graphics.Blit(src, dest, material);
  60. } else {
  61. Graphics.Blit(src, dest);
  62. }
  63. }
  64. private Matrix4x4 GetFrustumCornersRay() { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  65. Matrix4x4 frustumCorners = Matrix4x4.identity;
  66. float fov = cam.fieldOfView;
  67. float near = cam.nearClipPlane;
  68. float aspect = cam.aspect;
  69. float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
  70. Vector3 toRight = cam.transform.right * halfHeight * aspect; // 指向右方的向量
  71. Vector3 toTop = cam.transform.up * halfHeight; // 指向上方的向量
  72. Vector3 toForward = cam.transform.forward * near; // 指向前方的向量
  73. Vector3 bottomLeft = (toForward - toTop - toRight) / near; // 指向左下角的射线
  74. Vector3 bottomRight = (toForward + toRight - toTop) / near; // 指向右下角的射线
  75. Vector3 topRight = (toForward + toRight + toTop) / near; // 指向右上角的射线
  76. Vector3 topLeft = (toForward + toTop - toRight) / near; // 指向左上角的射线
  77. frustumCorners.SetRow(0, bottomLeft);
  78. frustumCorners.SetRow(1, bottomRight);
  79. frustumCorners.SetRow(2, topRight);
  80. frustumCorners.SetRow(3, topLeft);
  81. return frustumCorners;
  82. }
  83. }

? LaserRadar.shader

  1. Shader "MyShader/LaserRadar" { // 雷达波特效
  2. Properties{
  3. _MainTex("Base (RGB)", 2D) = "white" {} // 主纹理
  4. _Enable("Enable", Int) = 0 // 是否启动雷达波特效
  5. _WaveColor("WaveColor", Color) = (1, 0, 0, 1) // 雷达波的颜色
  6. _WaveLineWidth("WaveLineWidth", Float) = 0.3 // 雷达波纹的宽度
  7. _WaveCenter("WaveCenter", Vector) = (0, 0, 0, 0) // 雷达的波中心
  8. _WaveSpeed("WaveSpeed", Float) = 1 // 雷达波的传播速度
  9. _WaveTime("WaveTime", Float) = 0 // 雷达波传播的时间
  10. _WaveCastTime("WaveCastTime", Float) = 5 // 雷达波发射的时间周期
  11. _InitWaveDist("InitWaveDist", Float) = 3 // 雷达波初始的距离
  12. _MaxWaveDist("MaxWaveDist", Float) = 30 // 雷达波传播的最远距离
  13. }
  14. SubShader{
  15. Pass {
  16. // 深度测试始终通过, 关闭深度写入
  17. ZTest Always ZWrite Off
  18. CGPROGRAM
  19. #include "UnityCG.cginc"
  20. #pragma vertex vert
  21. #pragma fragment frag
  22. sampler2D _MainTex; // 主纹理
  23. sampler2D _CameraDepthTexture; // 深度纹理
  24. float4x4 _FrustumCornersRay; // 视锥体四角射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  25. int _Enable; // 是否启动雷达波特效
  26. fixed4 _WaveColor; // 雷达波的颜色
  27. float _WaveLineWidth; // 雷达波纹的宽度
  28. float4 _WaveCenter; // 雷达波的中心
  29. float _WaveSpeed; // 雷达波的速度
  30. float _WaveTime; // 雷达波传播的时间
  31. float _WaveCastTime; // 雷达波发射的时间周期
  32. float _InitWaveDist; // 雷达波初始的距离
  33. float _MaxWaveDist; // 雷达波传播的最远距离
  34. struct v2f {
  35. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
  36. half2 uv : TEXCOORD0; // 纹理uv坐标
  37. float4 interpolatedRay : TEXCOORD1; // 插值射线向量(由相机指向**面上点的向量除以near后的坐标)
  38. };
  39. float4 getInterpolatedRay(half2 uv) { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  40. int index = 0;
  41. if (uv.x < 0.5 && uv.y < 0.5) {
  42. index = 0;
  43. } else if (uv.x > 0.5 && uv.y < 0.5) {
  44. index = 1;
  45. } else if (uv.x > 0.5 && uv.y > 0.5) {
  46. index = 2;
  47. } else {
  48. index = 3;
  49. }
  50. return _FrustumCornersRay[index];
  51. }
  52. v2f vert(appdata_img v) {
  53. v2f o;
  54. o.pos = UnityObjectToClipPos(v.vertex); // 计算裁剪坐标系中顶点坐标, 等价于: mul(unity_MatrixMVP, v.vertex)
  55. o.uv = v.texcoord;
  56. o.interpolatedRay = getInterpolatedRay(v.texcoord); // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  57. return o;
  58. }
  59. fixed4 frag(v2f i) : SV_Target {
  60. if (_Enable == 0) {
  61. return tex2D(_MainTex, i.uv);
  62. }
  63. float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); // 非线性的深度, tex2D(_CameraDepthTexture, i.uv).r
  64. float linearDepth = LinearEyeDepth(depth); // 线性的深度
  65. float factor = 1;
  66. if (linearDepth < _ProjectionParams.z - 1) { // _ProjectionParams = (1, near, far, 1 / far)
  67. float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz; // 顶点世界坐标
  68. float len = length(worldPos - _WaveCenter.xyz); // 当前顶点距离雷达波中心的距离
  69. if (len < _InitWaveDist || len > _MaxWaveDist) {
  70. return tex2D(_MainTex, i.uv);
  71. }
  72. float time = fmod(_WaveTime, _WaveCastTime); // 当前发射周期中, 雷达波传播的时间
  73. float waveGap = _InitWaveDist + _WaveSpeed * time; // 当前发射周期中, 雷达波传播的距离
  74. float mod = fmod(len, waveGap); // 当前顶点距离最*的内环波纹的距离
  75. float rate = min(min(mod, waveGap - mod), _WaveLineWidth) / _WaveLineWidth; // 当前顶点处在波纹范围外的比率
  76. factor = smoothstep(0, 1, rate); // 当前顶点被采样为目标纹理颜色的比率因子
  77. }
  78. fixed4 tex = tex2D(_MainTex, i.uv);
  79. fixed4 color = lerp(_WaveColor, tex, factor);
  80. return color;
  81. }
  82. ENDCG
  83. }
  84. }
  85. FallBack off
  86. }

? 运行效果如下:

img

3.3 雷达波中心跟随物体运动

? LaserRadar.cs

  1. using UnityEngine;
  2. [RequireComponent(typeof(Camera))] // 需要相机组件
  3. public class LaserRadar : MonoBehaviour {
  4. public Color waveColor = Color.red; // 雷达波的颜色
  5. [Range(0.1f, 0.49f)]
  6. public float waveLineWidth = 0.49f; // 雷达波纹的宽度
  7. [Range(0.5f, 10f)]
  8. public float waveSpeed = 1f; // 雷达波传播的速度
  9. [Range(3, 10)]
  10. public float waveCastTime = 5; // 雷达波发射的时间周期
  11. [Range(0.1f, 20)]
  12. public float initWaveDist = 3; // 雷达波初始的距离
  13. [Range(10, 200)]
  14. public float maxWaveDist = 30f; // 雷达波传播的最远距离
  15. private bool enableWave = false; // 是否开启雷达波特效
  16. private Vector4 waveCenter; // 雷达波中心
  17. private Camera cam; // 相机
  18. private Material material = null; // 材质
  19. private Transform target; // 发射雷达波的目标物体
  20. private void Awake() {
  21. cam = GetComponent<Camera>();
  22. material = new Material(Shader.Find("MyShader/LaserRadar"));
  23. material.hideFlags = HideFlags.DontSave;
  24. target = GameObject.Find("Car").transform;
  25. }
  26. private void OnEnable() {
  27. cam.depthTextureMode |= DepthTextureMode.Depth;
  28. enableWave = true;
  29. material.SetInt("_Enable", 1);
  30. }
  31. private void OnRenderImage(RenderTexture src, RenderTexture dest) {
  32. if (enableWave) {
  33. Matrix4x4 frustumCorners = GetFrustumCornersRay();
  34. material.SetMatrix("_FrustumCornersRay", frustumCorners);
  35. material.SetColor("_WaveColor", waveColor);
  36. waveCenter = target.position;
  37. material.SetVector("_WaveCenter", waveCenter);
  38. material.SetFloat("_WaveLineWidth", waveLineWidth);
  39. material.SetFloat("_WaveSpeed", waveSpeed);
  40. material.SetFloat("_WaveCastTime", waveCastTime);
  41. material.SetFloat("_InitWaveDist", initWaveDist);
  42. material.SetFloat("_MaxWaveDist", maxWaveDist);
  43. Graphics.Blit(src, dest, material);
  44. } else {
  45. Graphics.Blit(src, dest);
  46. }
  47. }
  48. private Matrix4x4 GetFrustumCornersRay() { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  49. Matrix4x4 frustumCorners = Matrix4x4.identity;
  50. float fov = cam.fieldOfView;
  51. float near = cam.nearClipPlane;
  52. float aspect = cam.aspect;
  53. float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
  54. Vector3 toRight = cam.transform.right * halfHeight * aspect; // 指向右方的向量
  55. Vector3 toTop = cam.transform.up * halfHeight; // 指向上方的向量
  56. Vector3 toForward = cam.transform.forward * near; // 指向前方的向量
  57. Vector3 bottomLeft = (toForward - toTop - toRight) / near; // 指向左下角的射线
  58. Vector3 bottomRight = (toForward + toRight - toTop) / near; // 指向右下角的射线
  59. Vector3 topRight = (toForward + toRight + toTop) / near; // 指向右上角的射线
  60. Vector3 topLeft = (toForward + toTop - toRight) / near; // 指向左上角的射线
  61. frustumCorners.SetRow(0, bottomLeft);
  62. frustumCorners.SetRow(1, bottomRight);
  63. frustumCorners.SetRow(2, topRight);
  64. frustumCorners.SetRow(3, topLeft);
  65. return frustumCorners;
  66. }
  67. }

? LaserRadar.shader

  1. Shader "MyShader/LaserRadar" { // 雷达波特效
  2. Properties{
  3. _MainTex("Base (RGB)", 2D) = "white" {} // 主纹理
  4. _Enable("Enable", Int) = 0 // 是否启动雷达波特效
  5. _WaveColor("WaveColor", Color) = (1, 0, 0, 1) // 雷达波的颜色
  6. _WaveLineWidth("WaveLineWidth", Float) = 0.49 // 雷达波纹的宽度
  7. _WaveCenter("WaveCenter", Vector) = (0, 0, 0, 0) // 雷达的波中心
  8. _WaveSpeed("WaveSpeed", Float) = 1 // 雷达波的传播速度
  9. _WaveCastTime("WaveCastTime", Float) = 5 // 雷达波发射的时间周期
  10. _InitWaveDist("InitWaveDist", Float) = 3 // 雷达波初始的距离
  11. _MaxWaveDist("MaxWaveDist", Float) = 30 // 雷达波传播的最远距离
  12. }
  13. SubShader{
  14. Pass {
  15. // 深度测试始终通过, 关闭深度写入
  16. ZTest Always ZWrite Off
  17. CGPROGRAM
  18. #include "UnityCG.cginc"
  19. #pragma vertex vert
  20. #pragma fragment frag
  21. sampler2D _MainTex; // 主纹理
  22. sampler2D _CameraDepthTexture; // 深度纹理
  23. float4x4 _FrustumCornersRay; // 视锥体四角射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  24. int _Enable; // 是否启动雷达波特效
  25. fixed4 _WaveColor; // 雷达波的颜色
  26. float _WaveLineWidth; // 雷达波纹的宽度
  27. float4 _WaveCenter; // 雷达波的中心
  28. float _WaveSpeed; // 雷达波的速度
  29. float _WaveCastTime; // 雷达波发射的时间周期
  30. float _InitWaveDist; // 雷达波初始的距离
  31. float _MaxWaveDist; // 雷达波传播的最远距离
  32. struct v2f {
  33. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
  34. half2 uv : TEXCOORD0; // 纹理uv坐标
  35. float4 interpolatedRay : TEXCOORD1; // 插值射线向量(由相机指向**面上点的向量除以near后的坐标)
  36. };
  37. float4 getInterpolatedRay(half2 uv) { // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  38. int index = 0;
  39. if (uv.x < 0.5 && uv.y < 0.5) {
  40. index = 0;
  41. }
  42. else if (uv.x > 0.5 && uv.y < 0.5) {
  43. index = 1;
  44. } else if (uv.x > 0.5 && uv.y > 0.5) {
  45. index = 2;
  46. } else {
  47. index = 3;
  48. }
  49. return _FrustumCornersRay[index];
  50. }
  51. v2f vert(appdata_img v) {
  52. v2f o;
  53. o.pos = UnityObjectToClipPos(v.vertex); // 计算裁剪坐标系中顶点坐标, 等价于: mul(unity_MatrixMVP, v.vertex)
  54. o.uv = v.texcoord;
  55. o.interpolatedRay = getInterpolatedRay(v.texcoord); // 获取插值射线向量(由相机指向**面上四个角点的向量除以near后的坐标)
  56. return o;
  57. }
  58. fixed4 frag(v2f i) : SV_Target {
  59. if (_Enable == 0) {
  60. return tex2D(_MainTex, i.uv);
  61. }
  62. float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); // 非线性的深度, tex2D(_CameraDepthTexture, i.uv).r
  63. float linearDepth = LinearEyeDepth(depth); // 线性的深度
  64. float factor = 1;
  65. if (linearDepth < _ProjectionParams.z - 1) { // _ProjectionParams = (1, near, far, 1 / far)
  66. float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz; // 顶点世界坐标
  67. float len = length(worldPos - _WaveCenter.xyz); // 当前顶点距离雷达波中心的距离
  68. if (len < _InitWaveDist || len > _MaxWaveDist) {
  69. return tex2D(_MainTex, i.uv);
  70. }
  71. float time = fmod(_Time.y, _WaveCastTime); // 当前发射周期中, 雷达波传播的时间, _Time = (t/20, t, t*2, t*3)
  72. float waveGap = _InitWaveDist + _WaveSpeed * time; // 当前发射周期中, 雷达波传播的距离
  73. float mod = fmod(len, waveGap); // 当前顶点距离最*的内环波纹的距离
  74. float rate = min(min(mod, waveGap - mod), _WaveLineWidth) / _WaveLineWidth; // 当前顶点处在波纹范围外的比率
  75. factor = smoothstep(0, 1, rate); // 当前顶点被采样为目标纹理颜色的比率因子
  76. }
  77. fixed4 tex = tex2D(_MainTex, i.uv);
  78. fixed4 color = lerp(_WaveColor, tex, factor);
  79. return color;
  80. }
  81. ENDCG
  82. }
  83. }
  84. FallBack off
  85. }

? 运行效果如下:

img

? 声明:本文转自【Unity3D】激光雷达特效

原文链接:https://www.cnblogs.com/zhyan8/p/17615602.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号