【Unity3D】Jobs、Burst并行计算裁剪Texture3D物体

news2025/2/12 23:12:40

版本:Unity2019.4.0f1

PackageManager下载Burst插件(1.2.3版本)

利用如下代码,生成一个Texture3D资源,它只能脚本生成,是一个32*32*32的立方体,导出路径记得改下,不然报错。

using UnityEditor;
using UnityEngine;

public class ExampleEditorScript
{
    [MenuItem("CreateExamples/3DTexture")]
    static void CreateTexture3D()
    {
        // 配置纹理
        int size = 32;
        TextureFormat format = TextureFormat.RGBA32;
        TextureWrapMode wrapMode = TextureWrapMode.Clamp;

        // 创建纹理并应用配置
        Texture3D texture = new Texture3D(size, size, size, format, false);
        texture.wrapMode = wrapMode;

        // 创建 3 维数组以存储颜色数据
        Color[] colors = new Color[size * size * size];

        // 填充数组,使纹理的 x、y 和 z 值映射为红色、蓝色和绿色
        float inverseResolution = 1.0f / (size - 1.0f);
        for (int z = 0; z < size; z++)
        {
            int zOffset = z * size * size;
            for (int y = 0; y < size; y++)
            {
                int yOffset = y * size;
                for (int x = 0; x < size; x++)
                {
                    colors[x + yOffset + zOffset] = new Color(x * inverseResolution,
                        y * inverseResolution, z * inverseResolution, 1.0f);
                }
            }
        }

        // 将颜色值复制到纹理
        texture.SetPixels(colors);

        // 将更改应用到纹理,然后将更新的纹理上传到 GPU
        texture.Apply();

        // 将纹理保存到 Unity 项目
        AssetDatabase.CreateAsset(texture, "Assets/JobsDemo/Example3DTexture.asset");
    }
}

场景上创建一个Cube和LineRenderer(注意Line的位置要设置到(0,0,0) 如下图 摄像机保持位置(0,1,-10))

新建一个材质球挂到Cube上,Shader代码如下:

Shader "Unlit/VolumeShader"
{
    Properties
    {
        _MainTex("Texture", 3D) = "white" {}
        _Alpha("Alpha", float) = 0.02
        _StepSize("Step Size", float) = 0.01
    }
        SubShader
        {
            Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
            Blend One OneMinusSrcAlpha
            LOD 100

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

                // 最大光线追踪样本数
                #define MAX_STEP_COUNT 128

                // 允许的浮点数误差
                #define EPSILON 0.00001f

                struct appdata
                {
                    float4 vertex : POSITION;
                };

                struct v2f
                {
                    float4 vertex : SV_POSITION;
                    float3 objectVertex : TEXCOORD0;
                    float3 vectorToSurface : TEXCOORD1;
                };

                sampler3D _MainTex;
                float4 _MainTex_ST;
                float _Alpha;
                float _StepSize;

                v2f vert(appdata v)
                {
                    v2f o;

                    // 对象空间中的顶点将成为光线追踪的起点
                    o.objectVertex = v.vertex;

                    // 计算世界空间中从摄像机到顶点的矢量
                    float3 worldVertex = mul(unity_ObjectToWorld, v.vertex).xyz;
                    o.vectorToSurface = worldVertex - _WorldSpaceCameraPos;

                    o.vertex = UnityObjectToClipPos(v.vertex);
                    return o;
                }

                float4 BlendUnder(float4 color, float4 newColor)
                {
                    color.rgb += (1.0 - color.a) * newColor.a * newColor.rgb;
                    color.a += (1.0 - color.a) * newColor.a;
                    return color;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    // 开始在对象的正面进行光线追踪
                    float3 rayOrigin = i.objectVertex;

                    // 使用摄像机到对象表面的矢量获取射线方向
                    float3 rayDirection = mul(unity_WorldToObject, float4(normalize(i.vectorToSurface), 1));

                    float4 color = float4(0, 0, 0, 0);
                    float3 samplePosition = rayOrigin;

                    // 穿过对象空间进行光线追踪
                    for (int i = 0; i < MAX_STEP_COUNT; i++)
                    {
                        // 仅在单位立方体边界内累积颜色
                        if (max(abs(samplePosition.x), max(abs(samplePosition.y), abs(samplePosition.z))) < 0.5f + EPSILON)
                        {
                            float4 sampledColor = tex3D(_MainTex, samplePosition + float3(0.5f, 0.5f, 0.5f));
                            sampledColor.a *= _Alpha;
                            color = BlendUnder(color, sampledColor);
                            samplePosition += rayDirection * _StepSize;
                        }
                    }

                    return color;
                }
                ENDCG
            }
        }
}

新建一个空物体Jobs,挂载脚本JobsTest.cs

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;

public class JobsTest : MonoBehaviour
{
    private int width, height, depth;
    public LineRenderer lineRenderer;
    public GameObject cubeGo;
    private Transform cubeTrans;
    private Texture3D _Tex3D;
    private Color[] colors;
    private Color[] cacheColor;

    NativeArray<Color> nativeColors;
    NativeArray<ColorData> nativeColorDatas;
    MyJob myJob = new MyJob();

    private void Awake()
    {
        Material mat = cubeGo.GetComponent<MeshRenderer>().sharedMaterial;
        _Tex3D = (Texture3D)mat.GetTexture("_MainTex");
        width = _Tex3D.width;
        height = _Tex3D.height;
        depth = _Tex3D.depth;
        colors = _Tex3D.GetPixels();
        cacheColor = _Tex3D.GetPixels();
        cubeTrans = cubeGo.transform;
        Debug.Log(colors.Length);
    }

    private void OnEnable()
    {
        lineRenderer.positionCount = 1;
        _Tex3D.SetPixels(cacheColor);
        _Tex3D.Apply();

        nativeColors = new NativeArray<Color>(colors.Length, Allocator.Persistent);
        nativeColorDatas = new NativeArray<ColorData>(colors.Length, Allocator.Persistent);

        myJob.width = width;
        myJob.height = height;
        myJob.depth = depth;

        myJob.colors = nativeColors;
        myJob.colorDatas = nativeColorDatas;

        for (int z = 0; z < depth; z++)
        {
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int i = z * (width * height) + y * width + x;
                    nativeColors[i] = colors[i];

                    ColorData colorData = new ColorData();
                    colorData.x = x;
                    colorData.y = y;
                    colorData.z = z;
                    nativeColorDatas[i] = colorData;
                }
            }
        }
    }

    private void OnDisable()
    {
        _Tex3D.SetPixels(cacheColor);
        _Tex3D.Apply();

        nativeColorDatas.Dispose();
        nativeColors.Dispose();

    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Vector3 screenPos = Input.mousePosition;
            screenPos.z = 1;
            lineRenderer.SetPosition(lineRenderer.positionCount - 1, Camera.main.ScreenToWorldPoint(screenPos));

            if (lineRenderer.positionCount == 3)
            {
                Calculate();

                lineRenderer.positionCount = 1;
            }
            else
            {
                lineRenderer.positionCount++;
            }
        }
        else
        {
            if (lineRenderer.positionCount > 1)
            {
                Vector3 screenPos = Input.mousePosition;
                screenPos.z = 1;
                lineRenderer.SetPosition(lineRenderer.positionCount - 1, Camera.main.ScreenToWorldPoint(screenPos));
            }
        }
    }

    private void Calculate()
    {
        float startTime = Time.realtimeSinceStartup;
        //模型坐标
        myJob.p1 = cubeTrans.InverseTransformPoint(lineRenderer.GetPosition(0));
        myJob.p2 = cubeTrans.InverseTransformPoint(lineRenderer.GetPosition(1));
        myJob.p3 = cubeTrans.InverseTransformPoint(lineRenderer.GetPosition(2));

        myJob.object2World = cubeTrans.localToWorldMatrix;
        myJob.world2Camera = Camera.main.worldToCameraMatrix;
        myJob.camera2Clip = Camera.main.projectionMatrix;

        JobHandle jobHandle = default;
        jobHandle = myJob.ScheduleParallel(colors.Length, 64, jobHandle);
        jobHandle.Complete();

        _Tex3D.SetPixels(nativeColors.ToArray());
        _Tex3D.Apply();

        Debug.Log((Time.realtimeSinceStartup - startTime) * 1000 + "ms");
    }
}

[BurstCompile]
public struct MyJob : IJobFor
{
    //[NativeDisableContainerSafetyRestriction]
    public NativeArray<Color> colors;

    //[NativeDisableContainerSafetyRestriction] //发现jobs日志有 out of length报错可用此特性忽略
    public NativeArray<ColorData> colorDatas;

    public Vector3 p1, p2, p3;
    public int width, height, depth;

    public Matrix4x4 object2World;
    public Matrix4x4 world2Camera;
    public Matrix4x4 camera2Clip;
    public void Execute(int index)
    {
        if (colors[index] == Color.clear)
        {
            return;
        }
        Vector3 localPoint = new Vector3(colorDatas[index].x / (width * 1.0f), colorDatas[index].y / (height * 1.0f), colorDatas[index].z / (depth * 1.0f)) - (Vector3.one * 0.5f);
        Vector2 screenPoint = Local2Screen(localPoint);

        Vector2 screenP1 = Local2Screen(p1);
        Vector2 screenP2 = Local2Screen(p2);
        Vector2 screenP3 = Local2Screen(p3);

        bool isInside = IsPointInTriangle(screenPoint, screenP1, screenP2, screenP3);
        if (isInside)
        {
            colors[index] = Color.clear;
        }
    }

    //2个二维向量行列式值,可理解为求出了2个二维向量构成的面的法线z值
    private float Cross(Vector2 a, Vector2 b, Vector2 p)
    {
        return (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x);
    }

    private bool IsPointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
    {
        float signOfTrig = Cross(a, b, c);
        float signOfAB = Cross(a, b, p);
        float signOfCA = Cross(c, a, p);
        float signOfBC = Cross(b, c, p);
        bool d1 = (signOfAB * signOfTrig > 0);
        bool d2 = (signOfCA * signOfTrig > 0);
        bool d3 = (signOfBC * signOfTrig > 0);
        return d1 && d2 && d3;

        //方法2:
        //Vector3 pa = a - p;
        //Vector3 pb = b - p;
        //Vector3 pc = c - p;

        //分别进行3次,求其中2个向量构成的三角面的法线;
        //Vector3 pab = Vector3.Cross(pa, pb);
        //Vector3 pbc = Vector3.Cross(pb, pc);
        //Vector3 pca = Vector3.Cross(pc, pa);

        //分别进行3次,求其中2个法线构成的点积(夹角)>0代表两条法线方向相同
        //float z1 = Vector3.Dot(pab, pbc);
        //float z2 = Vector3.Dot(pab, pca);
        //float z3 = Vector3.Dot(pbc, pca);

        //若3条法线之间的朝向都是相同的,说明p点在<a,b,c>三角形内
        //return z1 > 0 && z2 > 0 && z3 > 0;            
    }

    private Vector2 Local2Screen(Vector3 localPos)
    {
        Vector3 worldPos = object2World.MultiplyPoint(localPos);
        Vector3 cameraPos = world2Camera.MultiplyPoint(worldPos);
        Vector4 clipPos = camera2Clip * new Vector4(cameraPos.x, cameraPos.y, cameraPos.z, 1.0f);
        if (clipPos.w != 0)
        {
            clipPos = clipPos / clipPos.w;
        }
        float screenX = (clipPos.x + 1) / 2f * 1920f;
        float screenY = (clipPos.y + 1) / 2f * 1080f;
        return new Vector2(screenX, screenY);
    }
}
public struct ColorData
{
    public float x, y, z;
}

项目资源:

耗时如下:

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

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

相关文章

轻量级安全云存储方案Hoodik

什么是 Hoodik &#xff1f; Hoodik 是一款轻量级、安全且自托管的云存储解决方案。它采用 Rust 和 Vue 设计和构建&#xff0c;专注于端到端加密&#xff0c;保护您的数据免受窥探和黑客的侵害。Hoodik 支持文件上传和下载&#xff0c;让您可以轻松地与其他用户共享文件。简单…

[WASAPI]音频API:从Qt MultipleMedia走到WASAPI,相似与不同

[WASAPI] 从Qt MultipleMedia 来看WASAPI 最近在学习有关Windows上的音频驱动相关的知识&#xff0c;在正式开始说WASAPI之前&#xff0c;我想先说一说Qt的Multiple Media&#xff0c;为什么呢&#xff1f;因为Qt的MultipleMedia实际上是WASAPI的一层封装&#xff0c;它在是线…

Linux 大文件管理与 Hugging Face 模型下载实践:解决磁盘空间与大文件传输的全攻略20241226

Linux 大文件管理与 Hugging Face 模型下载实践&#xff1a;解决磁盘空间与大文件传输的全攻略 引言 在 Linux 系统中管理大文件是一项常见但不容忽视的任务&#xff0c;尤其是在处理复杂场景时&#xff0c;比如磁盘空间不足、断点续传下载模型文件、管理日志文件等。通过实际…

TOGAF之架构标准规范-业务架构

TOGAF标准规范中&#xff0c;业务架构阶段的主要工作是开发支持架构愿景的业务架构。 如上所示&#xff0c;业务架构&#xff08;Business Architecture&#xff09;在TOGAF标准规范中处于B阶段&#xff0c;该阶段的主要内容包括阶段目标、阶段输入、流程步骤、架构方法。 阶段…

aPaaS是什么?有何特点?以及aPaaS核心优势有哪些?

​aPaaS是什么&#xff1f; aPaaS&#xff0c;Application Platform as aService&#xff0c;应用程序平台即服务。国际知名咨询机构 Gartner 对aPaaS所下的定义是&#xff1a;“这是基于PaaS(平台即服务)的一种解决方案&#xff0c;支持应用程序在云端的开发、部署和运行&…

【网络分析工具】WireShark的使用(超详细)

网络分析工具——WireShark的使用 简介WireShark软件安装Wireshark 开始抓包示例WireShark抓包界面WireShark 主要分为这几个界面TCP包的具体内容Wireshark过滤器设置wireshark过滤器表达式的规则Wireshark抓包分析TCP三次握手Wireshark分析常用操作 简介 WireShark是非常流…

前端js验证码插件

相关代码,在最上方的绑定资源

URDF文件中inertial数据的描述坐标系说明

这件事的来源是这样的&#xff1a;结构手动把连杆坐标系下描述的惯性张量数据写入了urdf中&#xff0c;给我到以后发现有问题&#xff0c;给我搞懵了&#xff0c;以为我错了这么多年&#xff0c;于是有了本次的深度调研&#xff0c;先上结论&#xff0c;感兴趣的可以参考后文。…

宠物行业的出路:在爱与陪伴中寻找增长新机遇

在当下的消费市场中&#xff0c;如果说有什么领域能够逆势而上&#xff0c;宠物行业无疑是一个亮点。当人们越来越注重生活品质和精神寄托时&#xff0c;宠物成为了许多人的重要伴侣。它们不仅仅是家庭的一员&#xff0c;更是情感的寄托和生活的调剂。然而&#xff0c;随着行业…

Web前端基础知识(三)

表单的应用非常丰富&#xff0c;可以说&#xff0c;每个网站都会用到表单。下面首先介绍表单中的form标签。 --------------------------------------------------------------------------------------------------------------------------------- <form></form&g…

学习C++:程序的注释

一&#xff0c;注释 作用&#xff1a;在代码种加一些说明和解释&#xff0c;方便自己或其他程序员阅读代码 1&#xff0c;单行注释&#xff1a;// 描述信息 通常放在一行代码的上方&#xff0c;或者一条语句的末尾&#xff0c;对该行代码说明。 2&#xff0c;多行注释&#x…

LAION-SG:一个大规模、高质量的场景图结构注释数据集,为图像-文本模型训练带来了革命性的进步。

2024-12-03&#xff0c;由浙江大学、江南大学、北京大学、阿里巴巴集团和蚂蚁集团联合创建的LAION-SG数据集&#xff0c;通过提供高质量的场景图&#xff08;SG&#xff09;结构注释&#xff0c;显著提升了复杂场景图像生成的性能&#xff0c;为图像-文本模型训练带来了革命性的…

低压降稳压器(LDO)典型特性压降

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、什么是压降 压降电压 VDO 是指为实现正常稳压&#xff0c;输入电压 VIN 必须高出所需输出电压 VOUT(nom) 的最小压差。请…

01 - 初识 Spring

初识Spring 企业级应用 企业级应用是指那些为商业组织、⼤型企业而创建并部署的解决⽅案及应用。这些⼤型的企业级应用结构复 杂、涉及的外部资源众多&#xff0c;事务密集&#xff0c;数据规模⼤&#xff0c;用户数量多&#xff0c;有较强的安全性考虑和较⾼的性能要求。 …

NLP 中文拼写检测纠正论文-04-Learning from the Dictionary

拼写纠正系列 NLP 中文拼写检测实现思路 NLP 中文拼写检测纠正算法整理 NLP 英文拼写算法&#xff0c;如果提升 100W 倍的性能&#xff1f; NLP 中文拼写检测纠正 Paper java 实现中英文拼写检查和错误纠正&#xff1f;可我只会写 CRUD 啊&#xff01; 一个提升英文单词拼…

【CSS in Depth 2 精译_091】15.4:让 CSS 高度值过渡到自动高度 + 15.5:自定义属性的过渡设置(全新)+ 15.6:本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 15 章 过渡】 ✔️ 15.1 状态间的由此及彼15.2 定时函数 15.2.1 定制贝塞尔曲线15.2.2 阶跃 15.3 非动画属性 15.3.1 不可添加动画效果的属性15.3.2 淡入与淡出 15.4 过…

PMP项目管理考试模拟真题及答案(中文版)

1、赶工一个任务时&#xff0c;你应该关注: A 尽可能多的任务。 B 非关键任务。 C 加速执行关键路径上的任务。 D通过成本最低化加速执行任务。 正确答案:C 2、“在对软件编码前我不能进行软件测试。”这句话说明了哪种依赖关系? A 随意的 B软逻辑关系 C 优先 D 强制…

CentOS7下的vsftpd服务器和客户端

目录 1、安装vsftpd服务器和ftp客户端&#xff1b; 2、配置vsftpd服务器&#xff0c;允许普通用户登录、下载、上传文件&#xff1b; 3、配置vsftpd服务器&#xff0c;允许anonymous用户登录、下载、上传文件&#xff1b; 4、配置vsftpd服务器&#xff0c;允许root用户登录…

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——12使用YOLO-Bin

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——12使用YOLO-Bin ​ 根据前面内容&#xff0c;所有的子任务已经基本结束&#xff0c;接下来就是调用转化的bin模型进行最后的逻辑控制了 1 .YOLO的bin使用 ​ 对于yolo其实有个简单的办法&#xff0c;也…

109.【C语言】数据结构之求二叉树的高度

目录 1.知识回顾&#xff1a;高度&#xff08;也称深度&#xff09; 2.分析 设计代码框架 返回左右子树高度较大的那个的写法一:if语句 返回左右子树高度较大的那个的写法二:三目操作符 3.代码 4.反思 问题 出问题的代码 改进后的代码 执行结果 1.知识回顾&#xf…