【ARFoundation自学03】平面追踪可视化效果美化

news2024/12/25 9:12:05

       对已检测到的平面默认的渲染效果显得有些生硬和突兀,有时我们需要更加友好、美观的的平面虚拟界面,这时就需要对已检测到的平面定制个性化的可视方案。为达到更好的视觉效果,处理的思路如下。

视觉效果前后对比!

(本节Shader与ARFeatheredPlaneMesh Visualizer.cs脚本来自Untiy官方AR Foudnation参考代码)

在实现本案例前需要先看第一篇,这篇文章搭建了平面检测的框架!本篇是在此基础上进行的升级!【ARFoundation自学01】搭建AR框架+检测平面+点击克隆立方体到地面=自信入门!_ar foundation教程-CSDN博客

一、 修改材质

(1)隐藏掉系统自带的黑色边框。

(2)重新制作一个渲染材质和Shader脚本

使用带Alpha通道半透明的PNG格式伯纹理,通过Shader脚本渲染这个半透明的纹理,将纹理透明区域镂空。

(3)编写一个渐隐的脚本,让边界的纹理渐隐

为了达到更好的视觉过渡。按照以上思路,我们直接对AR Default Plane预制体进行改造即可。

(1)删除AR Default Plane预制体上的Line Renderer组件。(这个是渲染黑色描边框的组件,删除就意味着不要描边)

(2)编写一个Shader代码,制作一张带Alpha通道半透明PNG格式纹理贴图在外部赋值给这个shader 代码!

Shader "Unlit/FeatheredPlaneShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _TexTintColor("Texture Tint Color", Color) = (1,1,1,1)
        _PlaneColor("Plane Color", Color) = (1,1,1,1)
        _ShortestUVMapping("Shortest UV Mapping Value", Range(0, 1)) = 1.0
    }

    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        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;
                float3 uv2 : TEXCOORD1;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 uv2 : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _TexTintColor;
            fixed4 _PlaneColor;
            float _ShortestUVMapping;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.uv2 = v.uv2;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * _TexTintColor;
                col = lerp(_PlaneColor, col, col.a);    
                col.a *= 1 - smoothstep(1, _ShortestUVMapping, i.uv2.x);
                return col;
            }
            ENDCG
        }
    }
}

这个是一个材质脚本,我们写好以后创建一个普通材质,把脚本直接拖给材质即可!

调一下参数!Unlit/FeatheredPlaneShader的材质进行如下操作:

  • 将在PS里面做好的透明纹理赋值给给_MainTex属性(Texture)。
  • _TexTintColor的Alpha值设置为220,确保十字星的透明部分得以正确显示。
  • _PlaneColor的Alpha值设置为0,使平面背景完全透明。

做好的材质赋值给平面预制体AR Default Plane

上面基本完成了可视化平面的视觉效果更换!但是还可以进一步优化,让边缘平滑渐隐! 

2.创建一个普通C#脚本-边缘平滑渐隐

该脚本主要实现平面的渲染,并处理边界使其平滑渐隐。将AR Feathered Plane Mesh Visualizer挂载到AR Default Plane预制体上!

完成之后的预制体组件脚本应该如图3-5所示。编译运行,找一个富纹理平面进行平面检测,效果如图3-6所示。相比AR Foundation自带的平面可视化,个性化后的渲染视觉效果要好很多,而且在平面边界处也有一个渐隐的平滑过渡。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

[RequireComponent(typeof(ARPlaneMeshVisualizer), typeof(MeshRenderer), typeof(ARPlane))]
public class ARFeatheredPlaneMeshVisualizer : MonoBehaviour
{
    [Tooltip("The width of the texture feathering (in world units).")]
    [SerializeField] float m_FeatheringWidth = 0.2f;
    public float featheringWidth
    {
        get { return m_FeatheringWidth; }
        set { m_FeatheringWidth = value; }
    }

    private ARPlaneMeshVisualizer m_PlaneMeshVisualizer;
    private ARPlane m_Plane;
    private MeshRenderer m_Renderer;
    private Material m_FeatheredPlaneMaterial;

    void Awake()
    {
        m_PlaneMeshVisualizer = GetComponent<ARPlaneMeshVisualizer>();
        m_Renderer = GetComponent<MeshRenderer>();
        m_FeatheredPlaneMaterial = m_Renderer.material;
        m_Plane = GetComponent<ARPlane>();
    }

    void OnEnable()
    {
        m_Plane.boundaryChanged += ARPlane_boundaryUpdated;
    }

    void OnDisable()
    {
        m_Plane.boundaryChanged -= ARPlane_boundaryUpdated;
    }

    void ARPlane_boundaryUpdated(ARPlaneBoundaryChangedEventArgs eventArgs)
    {
        GenerateBoundaryUVs(m_PlaneMeshVisualizer.mesh);
    }

    void GenerateBoundaryUVs(Mesh mesh)
    {
        int vertexCount = mesh.vertexCount;
        if (s_FeatheringUVs.Capacity < vertexCount) { s_FeatheringUVs.Capacity = vertexCount; }

        mesh.GetVertices(s_Vertices);

        Vector3 centerInPlaneSpace = Vector3.zero;
        for (int i = 0; i < vertexCount; i++)
        {
            centerInPlaneSpace += s_Vertices[i];
        }
        centerInPlaneSpace /= vertexCount;

        float shortestUVMapping = float.MaxValue;

        for (int i = 0; i < vertexCount; i++)
        {
            float vertexDist = Vector3.Distance(s_Vertices[i], centerInPlaneSpace);
            float uvMapping = vertexDist / Mathf.Max(vertexDist - featheringWidth, 0.001f);
            if (shortestUVMapping > uvMapping) { shortestUVMapping = uvMapping; }

            s_FeatheringUVs.Add(new Vector3(uvMapping, 0, 0));
        }

        m_FeatheredPlaneMaterial.SetFloat("_ShortestUVMapping", shortestUVMapping);

        mesh.SetUVs(1, s_FeatheringUVs);
        mesh.UploadMeshData(false);
    }

    static List<Vector3> s_FeatheringUVs = new List<Vector3>();
    static List<Vector3> s_Vertices = new List<Vector3>();
}

这是一个Unity C#脚本,名为`ARFeatheredPlaneMeshVisualizer`,用于为ARFoundation框架中的ARPlane对象添加一种特殊的视觉效果:根据平面边缘与中心的距离,为平面的顶点生成渐变的UV映射,从而实现类似羽毛边缘的过渡效果。该脚本依赖`ARPlaneMeshVisualizer`、`MeshRenderer`和`ARPlane`组件,并且通过响应ARPlane的边界变化事件来实时更新UV映射。下面为您详细解释脚本各部分的功能:

1. **命名空间和引用**:

   using System.Collections.Generic;
   using UnityEngine;
   using UnityEngine.XR.ARFoundation;
   ```
   引入所需命名空间,包括泛型集合、Unity基础类库以及ARFoundation相关API。

2. **类定义**:

   [RequireComponent(typeof(ARPlaneMeshVisualizer), typeof(MeshRenderer), typeof(ARPlane))]
   public class ARFeatheredPlaneMeshVisualizer : MonoBehaviour
   ```
   定义一个公共类`ARFeatheredPlaneMeshVisualizer`,继承自`MonoBehaviour`,并使用`[RequireComponent]`特性确保附着此脚本的游戏对象必须同时具有`ARPlaneMeshVisualizer`、`MeshRenderer`和`ARPlane`组件。

3. **属性定义**:

   [Tooltip("The width of the texture feathering (in world units).")]
   [SerializeField] float m_FeatheringWidth = 0.2f;
   public float FeatheringWidth
   {
       get { return m_FeatheringWidth; }
       set { m_FeatheringWidth = value; }
   }

   定义一个具有工具提示的私有变量`m_FeatheringWidth`,默认值为0.2f。使用`[SerializeField]`使其能在Unity Inspector面板中显示并编辑。同时提供一个公开的只读属性`FeatheringWidth`,用于外部访问和修改该值。`FeatheringWidth`决定了纹理边缘渐变过渡的宽度(以世界单位计)。

4. **成员变量**:

   private ARPlaneMeshVisualizer m_PlaneMeshVisualizer;
   private ARPlane m_Plane;
   private MeshRenderer m_Renderer;
   private Material m_FeatheredPlaneMaterial;
   ```
   定义私有变量,分别存储`ARPlaneMeshVisualizer`、`ARPlane`、`MeshRenderer`组件的引用以及关联的`Material`对象。

5. **Awake()**:

   void Awake()
   {
       m_PlaneMeshVisualizer = GetComponent<ARPlaneMeshVisualizer>();
       m_Renderer = GetComponent<MeshRenderer>();
       m_FeatheredPlaneMaterial = m_Renderer.material;
       m_Plane = GetComponent<ARPlane>();
   }
   `Awake()`方法在游戏对象初始化阶段调用,用于获取并存储所需组件的引用。

6. **生命周期方法**:

   void OnEnable()
   {
       m_Plane.boundaryChanged += ARPlane_boundaryUpdated;
   }

   void OnDisable()
   {
       m_Plane.boundaryChanged -= ARPlane_boundaryUpdated;
   }
   ```
   当组件启用时,注册`ARPlane.boundaryChanged`事件的处理函数`ARPlane_boundaryUpdated`。当组件禁用时,取消注册该事件,以避免内存泄漏和不必要的性能开销。

7. **ARPlane_boundaryUpdated()**:
   ```csharp
   void ARPlane_boundaryUpdated(ARPlaneBoundaryChangedEventArgs eventArgs)
   {
       GenerateBoundaryUVs(m_PlaneMeshVisualizer.mesh);
   }
   ```
   事件处理函数,当ARPlane的边界发生变化时调用。在此调用`GenerateBoundaryUVs`方法,传入`ARPlaneMeshVisualizer`组件提供的`Mesh`对象,以更新其UV映射。

8. **GenerateBoundaryUVs()**:
   ```csharp
   void GenerateBoundaryUVs(Mesh mesh)
   {
       // ... (详细解释见下文)
   }
   ```
   主要功能方法,负责计算并设置Mesh顶点的UV映射以实现羽毛边缘效果。下面详细解释内部逻辑:

   - 获取顶点数量和检查`s_FeatheringUVs`容量。
   - 使用`mesh.GetVertices`获取所有顶点坐标并存储在`s_Vertices`列表中。
   - 计算顶点集的中心点(质心)。
   - 初始化`shortestUVMapping`为最大浮点数,用于记录最小UV映射值。
   - 遍历每个顶点,计算其与中心点的距离(`vertexDist`),并据此计算UV映射值(`uvMapping`)。
     - UV映射值通过`vertexDist`与`featheringWidth`之差(取最大值为0.001f避免除以零)的比值得到,距离越远,UV映射值越大。
     - 如果当前UV映射值小于已记录的最小值,则更新`shortestUVMapping`。
   - 将计算得到的UV映射值(仅包含x分量,y和z设为0)添加到`s_FeatheringUVs`列表。
   - 将计算得到的最小UV映射值(`shortestUVMapping`)设置到材质的`_ShortestUVMapping`属性。
   - 使用`mesh.SetUVs`方法将`s_FeatheringUVs`列表中的UV映射值设置到Mesh的第二个UV通道(索引为1)。
   - 调用`mesh.UploadMeshData(false)`更新Mesh数据到GPU,完成UV映射设置。

9. **静态列表**:
   ```csharp
   static List<Vector3> s_FeatheringUVs = new List<Vector3>();
   static List<Vector3> s_Vertices = new List<Vector3>();
   ```
   定义两个静态列表,分别用于暂存计算得到的UV映射值和临时存储Mesh顶点坐标。

总结:`ARFeatheredPlaneMeshVisualizer`脚本为ARFoundation中的ARPlane添加了一种基于顶点距离的UV映射渐变效果,实现了类似羽毛边缘的过渡视觉效果。它监听ARPlane边界变化事件,动态更新Mesh的UV映射,确保在AR环境变化时能实时反映到渲染结果中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1693528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从cuda到cudnn到pytorch

一、预配版本信息 1、cuda12.1.1 2、cudnn8.9.7 3、pytorch2.2.0 二、引用 深度学习之环境配置&#xff1a;【CUDA 12.1.1cuDNN 8.9.1】最新安装教程记录 -- 20240429_torch 1.12.0对应torchvision-CSDN博客 补充&#xff1a; cuda历史版本索引&#xff1a; NVIDIA Dev…

只需一行代码提高3DGS重建质量!随机初始化新SOTA

论文标题&#xff1a; Relaxing Accurate Initialization Constraint for 3D Gaussian Splatting 论文作者&#xff1a; Jaewoo Jung, Jisang Han, Honggyu An, Jiwon Kang, Seonghoon Park and Seungryong Kim 导读&#xff1a; 3DGS在新视角合成和三维重建方面展现了令人印…

医疗科技:UWB模块为智能医疗设备带来的变革

随着医疗科技的不断发展和人们健康意识的提高&#xff0c;智能医疗设备的应用越来越广泛。超宽带&#xff08;UWB&#xff09;技术作为一种新兴的定位技术&#xff0c;正在引领着智能医疗设备的变革。UWB模块作为UWB技术的核心组成部分&#xff0c;在智能医疗设备中发挥着越来越…

【开发 | 环境配置】解决 VSCode 编写 eBPF 程序找不到头文件

问题描述&#xff1a; 在使用 vscode 编写 eBPF 程序时&#xff0c;如果不做一些头文件定位的操作&#xff0c;默认情况下头文件总是带有“红色下划线”&#xff0c;并且大部分的变量不会有提示与补全。 在编写代码文件较小时&#xff08;或者功能需求小时&#xff09;并不会…

Java虚拟机揭秘-底层驱动力,性能保障!

Java虚拟机作为Java技术体系的核心组成部分&#xff0c;其重要性不言而喻。它不仅为Java提供了跨平台的能力&#xff0c;更是Java程序运行的基石。本文将为您深入解析Java虚拟机的工作原理、作用和应用场景&#xff0c;并通过生动的实例让您彻底理解这一关键技术。 一、Java虚拟…

可视化 | Seaborn中的矩阵图及示例

Seaborn是python提供的一个很棒的可视化库。它有几种类型的绘图&#xff0c;通过这些绘图&#xff0c;它提供了惊人的可视化能力。其中一些包括计数图&#xff0c;散点图&#xff0c;配对图&#xff0c;回归图&#xff0c;矩阵图等等。本文讨论了Seaborn中的矩阵图。 示例1&am…

《计算机网络微课堂》2-2 物理层下面的传输媒体

请大家注意&#xff0c;传输媒体不属于计算机网络体系结构的任何一层&#xff0c;如果非要将它添加到体系结构中&#xff0c;‍‍那只能将其放在物理层之下。 传输媒体可分为两类&#xff1a;一类是导引型传输媒体&#xff0c;‍‍另一类是非导引型传输媒体。 在导引型传输媒体…

操作系统总结4----死锁的处理策略总结

目录 2.4.2 死锁的处理策略-----预防死锁 &#xff08;1&#xff09;知识总览 &#xff08;2&#xff09;破环互斥条件 &#xff08;3&#xff09;破环不剥夺条件 &#xff08;4&#xff09;破环求情和保持条件 &#xff08;5&#xff09;破环循环等待条件 总结 2.4.3 死…

使用Python Tkinter创建GUI应用程序

大家好&#xff0c;当我们谈及使用Python Tkinter创建GUI应用程序时&#xff0c;我们涉及的不仅是技术和代码&#xff0c;更是关于创造力和用户体验的故事。Tkinter作为Python标准库中最常用的GUI工具包&#xff0c;提供了丰富的功能和灵活的接口&#xff0c;让开发者能够轻松地…

《计算机网络微课堂》2-3 传输方式

本节课我们介绍几种传输方式&#xff1a; 串行传输和并行传输同步传输和异步传输单工&#xff0c;半双工‍‍以及全双工通信 ​​ ‍ 串行 我们首先来看串行传输和并行传输&#xff0c;串行传输是指‍‍数据是一个比特依次发送的&#xff0c;因此在发送端和接收端之间‍‍只…

YOLOv10 论文学习

论文链接&#xff1a;https://arxiv.org/pdf/2405.14458 代码链接&#xff1a;https://github.com/THU-MIG/yolov10 解决了什么问题&#xff1f; 实时目标检测是计算机视觉领域的研究焦点&#xff0c;目的是以较低的延迟准确地预测图像中各物体的类别和坐标。它广泛应用于自动…

速看!!!24上软考-信息系统项目管理师真题回忆,考点已更新

整理了24上半年软考高级信息系统项目管理师的考试真题&#xff0c;软考一个批次一套题&#xff0c;现在都是机考&#xff0c;收集题目比较困难&#xff0c;希望能给个小小的赞支持一下。 注意&#xff1a;当天考试的宝子们可以对答案预估分数&#xff01;后面场次的宝子可以提…

基于jeecgboot-vue3的Flowable新建流程定义(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、vue3版本因为流程分类是动态的&#xff0c;不再固定了&#xff0c;所以新建的时候需要选择建立哪种流程类型的流程 代码如下&#xff1a; <!-- 选择模型的流程类型对话框 -->&…

Spring Cloud学习笔记(Nacos):配置中心基础和代码样例

这是本人学习的总结&#xff0c;主要学习资料如下 - 马士兵教育 1、Overview2、样例2.1、Dependency2.2、配置文件的定位2.3、bootstrap.yml2.4、配置中心新增配置2.5、验证 1、Overview 配置中心用于管理配置项和配置文件&#xff0c;比如平时写的application.yml就是配置文件…

【MySQL】聊聊脏页flush的原理和控制策略

flush的时机 当更新一条SQL的时候&#xff0c;其实是先写undo日志&#xff0c;然后更新数据&#xff0c;二阶段写入redo 和 bin log。对于更新数据&#xff0c;其实是只修改了changer buffer中的数据&#xff0c;比如将name qxlxi, 但是磁盘数据页没有和内存页数据保持一致。…

删除指定目录

题目描述 我们定义一种目录结构字符串(类似Windows的 tree /f 的输出内容),用它来表达目录树的结构,如图所示: 目录结构字符串的输入仅含数字、字母和|-,其中:|- 表示子目录的层次符号;字母或数字组成目录名(字母大小写敏感)。 某一子目录挂接在其前面、最近的上一层…

【Linux系统编程】进程概念、进程排队、进程标识符、进程状态

目录 什么是进程&#xff1f; 浅谈进程排队 简述进程属性 进程属性之进程标识符 进程操作之进程创建 初识fork fork返回值 原理角度理解fork fork的应用 进程属性之进程状态 再谈进程排队 进程状态 运行状态 阻塞状态 挂起状态 Linux下的进程状态 “R”(运行状…

【Go专家编程——内存管理——垃圾回收】

垃圾回收 所谓的垃圾就上不在需要的内存块&#xff0c;垃圾如果不清理&#xff0c;这些内存块就没有办法再次被分配使用。在不支持垃圾回收的编程语言中&#xff0c;这些垃圾内存就上泄露的内存。 1. 垃圾回收算法 常见的垃圾回收算法有3种 引用计数&#xff1a;对每个对象…

SpringCloud的Config配置中心,为什么要分Server服务端和Client客户端?

SpringCloud的Config配置中心&#xff0c;为什么要分Server服务端和Client客户端&#xff1f; 在SpringCloud的Config配置中心中分了Server服务端和Client客户端&#xff0c;为什么需要这样分呢&#xff1f;它的思想是所有微服务的配置文件都放到git远程服务器上&#xff0c;让…

使用FFmpeg推流实现在B站24小时点歌直播

使用FFmpeg推流实现在B站24小时点歌直播 本文首发于个人博客 安装FFmpeg centos7 https://www.myfreax.com/how-to-install-ffmpeg-on-centos-7/ https://linuxize.com/post/how-to-install-ffmpeg-on-centos-7/ 使用FFmpeg在B站直播 https://zhuanlan.zhihu.com/p/2395…