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

版本: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;
}

项目资源:

耗时如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

紫光同创-盘古200pro+开发板

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 一、开发系统介绍 开发系统概述 MES2L676-200HP 开发板采用紫光同创 logos2 系列 FPGA&#xff0c;型号&#xff1a;…

【后端】LNMP环境搭建

长期更新各种好文&#xff0c;建议关注收藏&#xff01; 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root&#xff1a; sodu 或su mkdir path/{server,soft}…

VSCode设置Playwright教程

1.安装扩展 打开VS Code&#xff0c;在扩展—>搜索"Playwright Test for VSCode"&#xff0c;点击安装 按快捷键CommandShiftP&#xff0c;输入install playwright&#xff0c;点击安装Playwright 安装成功会有如下提示 2.调试脚本 打开tests/example.spec.ts文…

RK3566和Robo_C的EMC防护设计细节

USB部分的防护细节&#xff1a; ROBO C的USB接口&#xff1a; PF级别的电容滤波&#xff1a; TVS电容&#xff08;TVS Capacitor&#xff09;&#xff1a;用于与TVS二极管配合&#xff0c;保护电路免受瞬态电压冲击。电容一般较小&#xff0c;通常为几十皮法&#xff08;pF&am…

MicroDiffusion——采用新的掩码方法和改进的 Transformer 架构,实现了低预算的扩散模型

介绍 论文地址&#xff1a;https://arxiv.org/abs/2407.15811 现代图像生成模型擅长创建自然、高质量的内容&#xff0c;每年生成的图像超过十亿幅。然而&#xff0c;从头开始训练这些模型极其昂贵和耗时。文本到图像&#xff08;T2I&#xff09;扩散模型降低了部分计算成本&a…

使用 Three.js 创建一个 3D 人形机器人仿真系统

引言 在这篇文章中&#xff0c;我们将探讨如何使用 Three.js 创建一个简单但有趣的 3D 人形机器人仿真系统。这个机器人可以通过键盘控制进行行走和转向&#xff0c;并具有基本的动画效果。 技术栈 HTML5Three.jsJavaScript 实现步骤 1. 基础设置 首先&#xff0c;我们需要…

【c++高阶DS】最小生成树

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 目录 01.最小生成树Kruskal算法Prim算法 01.最小生成树 连通图中的每一棵生成树&#xff0c;都是原图的一个极大无环子图&#xff0c;即&#xff1a;从其中删去任何一条边&#xff0c;生成…

自学记录鸿蒙API 13:实现人脸比对Core Vision Face Comparator

完成了文本识别和人脸检测的项目后&#xff0c;我发现人脸比对是一个更有趣的一个小技术玩意儿。我决定整一整&#xff0c;也就是对HarmonyOS Next最新版本API 13中的Core Vision Face Comparator API的学习&#xff0c;这项技术能够对人脸进行高精度比对&#xff0c;并给出相似…

2024/12/29 黄冈师范学院计算机学院网络工程《路由期末复习作业一》

一、选择题 1.某公司为其一些远程小站点预留了网段 172.29.100.0/26&#xff0c;每一个站点有10个IP设备接到网络&#xff0c;下面那个VLSM掩码能够为该需求提供最小数量的主机数目 &#xff08; &#xff09; A./27 B./28 C./29 D./30 -首先审题我们需要搞清楚站点与网…

redis cluster集群

华子目录 什么是redis集群redis cluster的体系架构什么是数据sharding&#xff1f;什么是hash tag集群中删除或新增节点&#xff0c;数据如何迁移&#xff1f;redis集群如何使用gossip通信?定义meet信息ping消息pong消息fail消息&#xff08;不是用gossip协议实现的&#xff0…

PrimeVue菜单模块(Menu),看api的重要性

以下是对PrimeVue菜单模块&#xff08;Menu&#xff09;的API属性的中文详解&#xff1a; 一、整体概述 PrimeVue的菜单&#xff08;Menu&#xff09;是一个支持动态和静态定位的导航/命令组件&#xff0c;其API通过定义一些辅助的属性&#xff08;props&#xff09;、事件等&…

STM32中断详解

STM32中断详解 NVIC 中断系统中断向量表相关寄存器中断优先级中断配置 外部中断实验EXTI框图外部中断/事件线映射中断步骤初始化代码实现 定时器中断通用定时器相关功能标号1&#xff1a;时钟源标号 2&#xff1a;控制器标号 3&#xff1a;时基单元 代码实现 NVIC 中断系统 STM…

从零开始开发纯血鸿蒙应用之逻辑封装

从零开始开发纯血鸿蒙应用 一、前言二、逻辑封装的原则三、实现 FileUtil1、统一的存放位置2、文件的增删改查2.1、文件创建与文件保存2.2、文件读取2.2.1、读取内部文件2.2.2、读取外部文件 3、文件删除 四、总结 一、前言 应用的动态&#xff0c;借助 UI 响应完成&#xff0…

《机器学习》——线性回归模型

文章目录 线性回归模型简介一元线性回归模型多元线性回归模型误差项分析一元线性模型实例完整代码 多元线性模型实例完整代码 线性回归模型简介 线性回归是利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。 相关关系&…

【深度学习环境】NVIDIA Driver、Cuda和Pytorch(centos9机器,要用到显示器)

文章目录 一 、Anaconda install二、 NIVIDIA driver install三、 Cuda install四、Pytorch install 一 、Anaconda install Step 1 Go to the official website: https://www.anaconda.com/download Input your email and submit. Step 2 Select your version, and click i…

在HTML中使用Vue如何使用嵌套循环把集合中的对象集合中的对象元素取出来(我的意思是集合中还有一个集合那种)

在 Vue.js 中处理嵌套集合&#xff08;即集合中的对象包含另一个集合&#xff09;时&#xff0c;使用多重 v-for 指令来遍历这些层次结构。每个 v-for 指令可以用于迭代一个特定级别的数据集&#xff0c;并且可以在模板中嵌套多个 v-for 来访问更深层次的数据。 例如&#xff…

ip归属地是什么意思?ip归属地是实时定位吗

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识符&#xff0c;不仅关乎设备间的通信&#xff0c;还涉及到用户的网络身份与位置信息。其中&#xff0c;IP归属地作为IP地址的地理位置信息&#xff0c;备受用户关注。本文将详细解析IP归属地的含义&#xff0c;并探讨其是…

基于BP训练深度学习模型(用于回归)以及验证误差值

用原生Python训练了一个BP网络&#xff0c;适合没有pytorch等环境的电脑&#xff0c;并用训练的模型对原始数据进行了预测&#xff0c;拿来估测比较误差值了&#xff0c;可以直接拿去用&#xff08;需根据个人数据来调训练次数、学习效率&#xff09;&#xff0c;代码在文章末。…

C#冒泡排序

一、冒泡排序基本原理 冒泡排序是一种简单的排序算法。它重复地走访要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c;也就是说该数列已经排序完成。 以一个简单的整数数…

折腾日记:如何让吃灰笔记本发挥余热——搭建一个相册服务

背景 之前写过&#xff0c;我在家里用了一台旧的工作站笔记本做了服务器&#xff0c;连上一个绿联的5位硬盘盒实现简单的网盘功能&#xff0c;然而&#xff0c;还是觉的不太理想&#xff0c;比如使用filebrowser虽然可以备份文件和图片&#xff0c;当使用手机使用网页&#xf…