【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);// 将更改应用到纹理,然后将更新的纹理上传到 GPUtexture.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 OneMinusSrcAlphaLOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"// 最大光线追踪样本数#define MAX_STEP_COUNT 128// 允许的浮点数误差#define EPSILON 0.00001fstruct 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 framevoid 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.mzph.cn/news/891164.shtml

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

相关文章

最短路径-Dijkstra 算法

前言 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的&#xff0c;因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法&#xff0c;解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展&#xff0c;直到扩展到终点…

ESP32 I2S音频总线学习笔记(一):初识I2S通信与配置基础

文章目录 简介为什么需要I2S&#xff1f;关于音频信号采样率分辨率音频声道 怎样使用I2S传输音频&#xff1f;位时钟BCLK字时钟WS串行数据SD I2S传输模型I2S通信格式I2S格式左对齐格式右对齐格式 i2s基本配置i2s 底层API加载I2S驱动设置I2S使用的引脚I2S读取数据I2S发送数据卸载…

Eclipse中引入NS3项目

参考资料&#xff1a; 博主&#xff1a;深度不睡觉 NS3的3.36版本将Eclipse作IDE_ns3使用eclipse-CSDN博客 从1.2安装eclipse开始 其中参考教程中省略的几点&#xff1a; 1.下载解压tar包 mkdir /Tools/Eclipse/EclipseTool # 新建目录 tar -zxvf /path/to/eclipse-cpp-20…

机器学习周报-TCN文献阅读

文章目录 摘要Abstract 1 TCN通用架构1.1 序列建模任务描述1.2 因果卷积&#xff08;Causal Convolutions&#xff09;1.3 扩张卷积&#xff08;Dilated Convolutions&#xff09;1.4 残差连接&#xff08;Residual Connections&#xff09; 2 TCN vs RNN3 TCN缺点4 代码4.1 TC…

Quartz任务调度框架实现任务动态执行

说明&#xff1a;之前使用Quartz&#xff0c;都是写好Job&#xff0c;指定一个时间点&#xff0c;到点执行。最近有个需求&#xff0c;需要根据前端用户设置的时间点去执行&#xff0c;也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行…

Spring-kafka快速Demo示例

使用Spring-Kafka快速发送/接受Kafka消息示例代码&#xff0c;项目结构是最基础的SpringBoot结构&#xff0c;提前安装好Kafka&#xff0c;确保Kafka已经正确启动 pom.xml&#xff0c;根据个人情况更换springboot、java版本等 <?xml version"1.0" encoding&qu…

【C++】B2079 求出 e 的值

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目介绍输入格式输出格式输入输出样例说明/提示 &#x1f4af;实现方法一&#xff1a;单层 for 循环计算代码实现运行逻辑解析优点不足 &#x1f4af;实现方法二&#xff…

STM32配合可编程加密芯片SMEC88ST的防抄板加密方案设计

SMEC88ST SDK卡发包下载 目前市场上很多嵌入式产品方案都是可以破解复制的&#xff0c;主要是因为方案主芯片不具备防破解的功能&#xff0c;这就导致开发者投入大量精力、财力开发的新产品一上市就被别人复制&#xff0c;到市场上的只能以价格竞争&#xff0c;最后工厂复制的产…

精准识别花生豆:基于EfficientNetB0的深度学习检测与分类项目

精准检测花生豆&#xff1a;基于EfficientNet的深度学习分类项目 在现代农业生产中&#xff0c;作物的质量检测和分类是确保产品质量的重要环节。针对花生豆的检测与分类需求&#xff0c;我们开发了一套基于深度学习的解决方案&#xff0c;利用EfficientNetB0模型实现高效、准…

MarkItDown的使用(将Word、Excel、PDF等转换为Markdown格式)

MarkItDown的使用&#xff08;将Word、Excel、PDF等转换为Markdown格式&#xff09; 本文目录&#xff1a; 零、时光宝盒&#x1f33b; 一、简介 二、安装 三、使用方法 3.1、使用命令行形式 3.2、用 Python 调用 四、总结 五、参考资料 零、时光宝盒&#x1f33b; &a…

Qanything 2.0源码解析系列6 PDF解析逻辑

Qanything 2.0源码解析系列6: PDF解析逻辑 type: Post status: Published date: 2024/12/04 summary: 深入剖析Qanything是如何拆解PDF的,核心是pdf转markdown category: 技术分享 原文:www.feifeixu.top 😀 前言: 在前面的文章中探究了图片是怎么进行解析的,这篇文章对…

【Agent】Chatbot、Copilot与Agent如何帮助我们的提升效率?

人工智能&#xff08;AI&#xff09;技术的迅猛发展正在深刻改变我们的生活和工作方式。你是否曾想过&#xff0c;未来的工作场景会是什么样子&#xff1f;AI的崛起不仅仅是科技的进步&#xff0c;更是我们生活方式的革命。今天&#xff0c;我们将深入探讨三种主要的AI能力&…

如何使用python读写游戏内存以及使用特征码匹配基址

一.读写内存所需的基本参数 接下来我将使用GTA5游戏举例 1.通过进程名称获取进程pid from psutil import process_iterdef get_process_id_by_name(process_name):for process in process_iter(["pid", "name"]):if process.info["name"] pr…

简述css中z-index的作用?如何用定位使用?

z-index是一个css属性&#xff0c;用于控制元素的堆叠顺序&#xff0c; 如何使用定位用index 1、position&#xff1a;relative&#xff1b; z-index&#xff1b; 相对于自己来定位的&#xff0c;可以根据top&#xff0c;bottom&#xff0c;right&#xff0c;left&#xff…

CCNP_SEC_ASA 第六天作业

实验需求&#xff1a; 为保障内部用户能够访问Internet&#xff0c;请把10.1.1.0/24网络动态转换到外部地址池202.100.1.100-202.100.1.200&#xff0c;如果地址池耗尽后&#xff0c;PAT到Outside接口 提示&#xff1a;需要看到如下输出信息 Inside#telnet 202.100.1.1 Trying …

计算机网络 (13)信道复用技术

前言 计算机网络中的信道复用技术是一种提高网络资源利用率的关键技术。它允许在一条物理信道上同时传输多个用户的信号&#xff0c;从而提高了信道的传输效率和带宽利用率。 一、信道复用技术的定义 信道复用&#xff08;Multiplexing&#xff09;就是在一条传输媒体上同时传输…

敏捷开发Scrum的深入理解和实践

敏捷开发&#xff0c;特别是Scrum方法&#xff0c;已经逐渐成为软件开发领域的主流方法。Scrum不仅适用于软件开发&#xff0c;还适用于其他需要快速响应变化和灵活交付的领域。本文将深入探讨Scrum的核心概念、流程、优势、挑战及其在实践中的应用。 一、Scrum的核心概念 Scru…

计算机视觉目标检测-2

文章目录 摘要abstract1.Fast R-CNN1.1 RoI pooling1.2 End-to -End model1.3 多任务损失-Multi-task loss1.4 R-CNN、SPPNet、Fast R-CNN效果比对 2.Faster R-CNN2.1 RPN原理2.2 效果对比2.3 Faster R-CNN总结 3.总结4.参考文献 摘要 本周学习了Fast R-CNN和Faster R-CNN算法…

JavaScript网页设计案例:动态交互式任务列表

在现代网页开发中&#xff0c;JavaScript被广泛应用于实现动态交互效果。看完这一篇你就可以设计一个动态任务列表&#xff0c;全面展示HTML、CSS和JavaScript在前端开发中的实际应用。通过本案例&#xff0c;你将深入了解事件监听、DOM操作以及用户交互设计的实现过程。 案例需…

【MySQL】索引 面试题

文章目录 适合创建索引的情况创建索引的注意事项MySQL中不适合创建索引的情况索引失效的常见情况 索引定义与作用 索引是帮助MySQL高效获取数据的有序数据结构&#xff0c;通过维护特定查找算法的数据结构&#xff08;如B树&#xff09;&#xff0c;以某种方式引用数据&#xf…