Learn ComputeShader 13 Adding a mesh to each particle

news2024/9/25 3:17:15

这次要给每个粒子加上网格。

添加的网格只是一个简单的四边形,它需要分成两个三角形。并且三角形的顶点必须按照顺时针排列,同时每个顶点都应该有UV信息。

所以接下来就要添加顶点结构体以及相关的compute buffer

 struct Vertex
    {
        public Vector3 position; // 顶点的3D位置
        public Vector2 uv;       // 纹理坐标
        public float life;       // 生命周期
    }

    ComputeBuffer vertexBuffer;

接下来就要创建顶点数组并且初始化。这里只需要设置顶点数组的uv就可以了。位置信息后面通过computebuffer实时更新。然后传递buffer到两个shader中并且设置相关变量

        int numVertices = numParticles * 6;
        Vertex[] vertexArray = new Vertex[numVertices];

        Vector3 pos = new Vector3();

        int index;
        for (int i = 0; i < numParticles; i++)
        {
            pos.Set(Random.value * 2 - 1.0f, Random.value * 2 - 1.0f, Random.value * 2 - 1.0f);
            pos.Normalize();
            pos *= Random.value;
            pos *= 0.5f;

            particleArray[i].position.Set(pos.x, pos.y, pos.z + 3);
            particleArray[i].velocity.Set(0, 0, 0);

            // Initial life value
            particleArray[i].life = Random.value * 5.0f + 1.0f;

            index = i * 6;
            // Triangle 1 - bottom-left, top-left, top-right
            vertexArray[index].uv.Set(0, 0);
            vertexArray[index + 1].uv.Set(0, 1);
            vertexArray[index + 2].uv.Set(1, 1);

            // Triangle 2 - bottom-left, top-right, bottom-right
            vertexArray[index + 3].uv.Set(0, 0);
            vertexArray[index + 4].uv.Set(1, 1);
            vertexArray[index + 5].uv.Set(1, 0);

        }

        // create compute buffers
        particleBuffer = new ComputeBuffer(numParticles, SIZE_PARTICLE);
        particleBuffer.SetData(particleArray);
        vertexBuffer = new ComputeBuffer(numVertices, SIZE_VERTEX);
        vertexBuffer.SetData(vertexArray);

        // bind the compute buffers to the shader and the compute shader
        shader.SetBuffer(kernelID, "particleBuffer", particleBuffer);
        shader.SetBuffer(kernelID, "vertexBuffer", vertexBuffer);
        shader.SetFloat("halfSize", quadSize * 0.5f);
        material.SetBuffer("vertexBuffer", vertexBuffer);

然后去computeshader中更新顶点位置信息,其余内容都和上次一样,这里要记住三角形顶点是顺时针排列的。

int index = id.x * 6;

	// Triangle 1 - bottom-left, top-left, top-right
	vertexBuffer[index].position.x = p.position.x - halfSize;
	vertexBuffer[index].position.y = p.position.y - halfSize;
	vertexBuffer[index].position.z = p.position.z;
	vertexBuffer[index].life = p.life;

	vertexBuffer[index + 1].position.x = p.position.x - halfSize;
	vertexBuffer[index + 1].position.y = p.position.y + halfSize;
	vertexBuffer[index + 1].position.z = p.position.z;
	vertexBuffer[index + 1].life = p.life;

	vertexBuffer[index + 2].position.x = p.position.x + halfSize;
	vertexBuffer[index + 2].position.y = p.position.y + halfSize;
	vertexBuffer[index + 2].position.z = p.position.z;
	vertexBuffer[index + 2].life = p.life;

	// Triangle 2 - bottom-left, top-right, bottom-right
	vertexBuffer[index + 3].position.x = p.position.x - halfSize;
	vertexBuffer[index + 3].position.y = p.position.y - halfSize;
	vertexBuffer[index + 3].position.z = p.position.z;
	vertexBuffer[index + 3].life = p.life;

	vertexBuffer[index + 4].position.x = p.position.x + halfSize;
	vertexBuffer[index + 4].position.y = p.position.y + halfSize;
	vertexBuffer[index + 4].position.z = p.position.z;
	vertexBuffer[index + 4].life = p.life;

	vertexBuffer[index + 5].position.x = p.position.x + halfSize;
	vertexBuffer[index + 5].position.y = p.position.y - halfSize;
	vertexBuffer[index + 5].position.z = p.position.z;
	vertexBuffer[index + 5].life = p.life;

接着我们去到顶点着色器就可以获取到修改后的顶点信息,和上次一样的设置颜色,然后转换位置到裁剪空间,这次还要加上uv,因为我们一会要对提额图采样。这次不一样的是每一个instance有六个顶点,这是我们在onrenderobject中设置的。

v2f vert(uint vertex_id : SV_VertexID, uint instance_id : SV_InstanceID)
		{
			v2f o = (v2f)0;

			int index = instance_id * 6 + vertex_id;
			float lerpVal = vertexBuffer[index].life * 0.25;

			// 计算颜色值
			o.color = fixed4(
				1 - lerpVal + 0.1,  // Red component
				lerpVal + 0.1,      // Green component
				1,                  // Blue component
				lerpVal             // Alpha component
			);

			// 计算顶点的世界空间到裁剪空间的位置
			o.position = UnityWorldToClipPos(float4(vertexBuffer[index].position, 1));

			// 设置纹理坐标
			o.uv = vertexBuffer[index].uv;
			
			return o;
		}

接下来是片段着色器,只需要加上贴图采样

		float4 frag(v2f i) : COLOR
		{
			fixed4 color = tex2D(_MainTex,i.uv)*i.color;
			return color;
		}

目前效果:目前我们看不到五角星,尽管在shader中我们对五角星贴图进行了采样,这是因为我们没有设置混合模式。这样的话透明度就没有了作用,仍然会显示成正方形。

增加下面这些代码以后,就可以正常渲染了

		LOD 200
		Blend SrcAlpha OneMinusSrcAlpha
		ZWrite Off

 最终效果:

完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#pragma warning disable 0649

public class QuadParticles : MonoBehaviour
{

    private Vector2 cursorPos;

    // struct
    struct Particle
    {
        public Vector3 position;
        public Vector3 velocity;
        public float life;
    }

    struct Vertex
    {
        public Vector3 position; // 顶点的3D位置
        public Vector2 uv;       // 纹理坐标
        public float life;       // 生命周期
    }


    const int SIZE_PARTICLE = 7 * sizeof(float);
    const int SIZE_VERTEX = 6 * sizeof(float);

    public int particleCount = 10000;
    public Material material;
    public ComputeShader shader;
    [Range(0.01f, 1.0f)]
    public float quadSize = 0.1f;

    int numParticles;
    int numVerticesInMesh;
    int kernelID;
    ComputeBuffer particleBuffer;
    ComputeBuffer vertexBuffer;
    
    int groupSizeX; 
    
    // Use this for initialization
    void Start()
    {
        Init();
    }

    void Init()
    {
        // find the id of the kernel
        kernelID = shader.FindKernel("CSMain");

        uint threadsX;
        shader.GetKernelThreadGroupSizes(kernelID, out threadsX, out _, out _);
        groupSizeX = Mathf.CeilToInt((float)particleCount / (float)threadsX);
        numParticles = groupSizeX * (int)threadsX;

        // initialize the particles
        Particle[] particleArray = new Particle[numParticles];

        int numVertices = numParticles * 6;
        Vertex[] vertexArray = new Vertex[numVertices];

        Vector3 pos = new Vector3();

        int index;
        for (int i = 0; i < numParticles; i++)
        {
            pos.Set(Random.value * 2 - 1.0f, Random.value * 2 - 1.0f, Random.value * 2 - 1.0f);
            pos.Normalize();
            pos *= Random.value;
            pos *= 0.5f;

            particleArray[i].position.Set(pos.x, pos.y, pos.z + 3);
            particleArray[i].velocity.Set(0, 0, 0);

            // Initial life value
            particleArray[i].life = Random.value * 5.0f + 1.0f;

            index = i * 6;
            // Triangle 1 - bottom-left, top-left, top-right
            vertexArray[index].uv.Set(0, 0);
            vertexArray[index + 1].uv.Set(0, 1);
            vertexArray[index + 2].uv.Set(1, 1);

            // Triangle 2 - bottom-left, top-right, bottom-right
            vertexArray[index + 3].uv.Set(0, 0);
            vertexArray[index + 4].uv.Set(1, 1);
            vertexArray[index + 5].uv.Set(1, 0);

        }

        // create compute buffers
        particleBuffer = new ComputeBuffer(numParticles, SIZE_PARTICLE);
        particleBuffer.SetData(particleArray);
        vertexBuffer = new ComputeBuffer(numVertices, SIZE_VERTEX);
        vertexBuffer.SetData(vertexArray);

        // bind the compute buffers to the shader and the compute shader
        shader.SetBuffer(kernelID, "particleBuffer", particleBuffer);
        shader.SetBuffer(kernelID, "vertexBuffer", vertexBuffer);
        shader.SetFloat("halfSize", quadSize * 0.5f);
        material.SetBuffer("vertexBuffer", vertexBuffer);


    }

    void OnRenderObject()
    {
        material.SetPass(0);
        Graphics.DrawProceduralNow(MeshTopology.Triangles, 6, numParticles);
    }
    
    void OnDestroy()
    {
        if (particleBuffer != null){
            particleBuffer.Release();
        }
    }

    // Update is called once per frame
    void Update()
    {
        float[] mousePosition2D = { cursorPos.x, cursorPos.y };

        // Send datas to the compute shader
        shader.SetFloat("deltaTime", Time.deltaTime);
        shader.SetFloats("mousePosition", mousePosition2D);

        // Update the Particles
        shader.Dispatch(kernelID, groupSizeX, 1, 1);
    }

    void OnGUI()
    {
        Vector3 p = new Vector3();
        Camera c = Camera.main;
        Event e = Event.current;
        Vector2 mousePos = new Vector2();

        // Get the mouse position from Event.
        // Note that the y position from Event is inverted.
        mousePos.x = e.mousePosition.x;
        mousePos.y = c.pixelHeight - e.mousePosition.y;

        p = c.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, c.nearClipPlane + 14));

        cursorPos.x = p.x;
        cursorPos.y = p.y;
        
    }
}

Shader "Custom/QuadParticle" {
	Properties     
    {
        _MainTex("Texture", 2D) = "white" {}     
    }  

	SubShader {
		Pass {
		Tags{ "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" }
		LOD 200
		Blend SrcAlpha OneMinusSrcAlpha
		ZWrite Off

		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
		#pragma vertex vert
		#pragma fragment frag

		#include "UnityCG.cginc"

		// Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 5.0

		struct Vertex{
			float3 position;
			float2 uv;
			float life;
		};
		
		struct v2f{
			float4 position : SV_POSITION;
			float4 color : COLOR;
			float2 uv: TEXCOORD0;
			float life : LIFE;
		};
		// particles' data
		StructuredBuffer<Vertex> vertexBuffer;
		sampler2D _MainTex;
		
		v2f vert(uint vertex_id : SV_VertexID, uint instance_id : SV_InstanceID)
		{
			v2f o = (v2f)0;

			int index = instance_id*6 + vertex_id;
			float lerpVal = vertexBuffer[index].life * 0.25f;
			o.color = fixed4(1.0f - lerpVal+0.1, lerpVal+0.1, 1.0f, lerpVal);
			o.position = UnityWorldToClipPos(float4(vertexBuffer[index].position, 1.0f));
			o.uv = vertexBuffer[index].uv;

			return o;
		}

		float4 frag(v2f i) : COLOR
		{
			fixed4 color = tex2D( _MainTex, i.uv ) * i.color;
			return color;
		}


		ENDCG
		}
	}
	FallBack Off
}
#pragma kernel CSMain

 // Particle's data
struct Particle
{
	float3 position;
	float3 velocity;
	float life;
};
struct Vertex
{
	float3 position;
	float2 uv;
	float life;
};

// Particle's data, shared with the shader
RWStructuredBuffer<Particle> particleBuffer;
RWStructuredBuffer<Vertex> vertexBuffer;

// Variables set from the CPU 
float deltaTime;
float2 mousePosition;
float halfSize;

uint rng_state;

// http://www.reedbeta.com/blog/quick-and-easy-gpu-random-numbers-in-d3d11/
uint rand_xorshift()
{
	// Xorshift algorithm from George Marsaglia's paper
	rng_state ^= (rng_state << 13);
	rng_state ^= (rng_state >> 17);
	rng_state ^= (rng_state << 5);
	return rng_state;
}

void respawn(uint id)
{
	rng_state = id;
	float tmp = (1.0 / 4294967296.0);
	float f0 = float(rand_xorshift()) * tmp - 0.5;
	float f1 = float(rand_xorshift()) * tmp - 0.5;
	float f2 = float(rand_xorshift()) * tmp - 0.5;
	float3 normalF3 = normalize(float3(f0, f1, f2)) * 0.8f;
	normalF3 *= float(rand_xorshift()) * tmp;
	particleBuffer[id].position = float3(normalF3.x + mousePosition.x, normalF3.y + mousePosition.y, normalF3.z + 3.0);
	// reset the life of this particle
	particleBuffer[id].life = 4;
	particleBuffer[id].velocity = float3(0,0,0);
}

[numthreads(256, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
	Particle p = particleBuffer[id.x];
	
	// subtract the life based on deltaTime
	p.life -= deltaTime;

	float3 delta = float3(mousePosition.xy, 3) - p.position;
	float3 dir = normalize(delta);

	p.velocity += dir;
	p.position += p.velocity * deltaTime;

	particleBuffer[id.x] = p;
	
	if (particleBuffer[id.x].life < 0){
		respawn(id.x);
		p = particleBuffer[id.x];
	}
	
	//Set the vertex buffer //
	int index = id.x * 6;
	//Triangle 1 - bottom-left, top-left, top-right   
	vertexBuffer[index].position.x = p.position.x-halfSize;
	vertexBuffer[index].position.y = p.position.y-halfSize;
	vertexBuffer[index].position.z = p.position.z;
	vertexBuffer[index].life = p.life;
	vertexBuffer[index+1].position.x = p.position.x-halfSize;
	vertexBuffer[index+1].position.y = p.position.y+halfSize;
	vertexBuffer[index+1].position.z = p.position.z;
	vertexBuffer[index+1].life = p.life;
	vertexBuffer[index+2].position.x = p.position.x+halfSize;
	vertexBuffer[index+2].position.y = p.position.y+halfSize;
	vertexBuffer[index+2].position.z = p.position.z;
	vertexBuffer[index+2].life = p.life;
	//Triangle 2 - bottom-left, top-right, bottom-right  // // 
	vertexBuffer[index+3].position.x = p.position.x-halfSize;
	vertexBuffer[index+3].position.y = p.position.y-halfSize;
	vertexBuffer[index+3].position.z = p.position.z;
	vertexBuffer[index+3].life = p.life;
	vertexBuffer[index+4].position.x = p.position.x+halfSize;
	vertexBuffer[index+4].position.y = p.position.y+halfSize;
	vertexBuffer[index+4].position.z = p.position.z;
	vertexBuffer[index+4].life = p.life;
	vertexBuffer[index+5].position.x = p.position.x+halfSize;
	vertexBuffer[index+5].position.y = p.position.y-halfSize;
	vertexBuffer[index+5].position.z = p.position.z;
	vertexBuffer[index+5].life = p.life;
}


 

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

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

相关文章

大数据新视界 --大数据大厂之 Spark 性能优化秘籍:从配置到代码实践

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

数据结构————栈的讲解(超详细!!!)

1 栈的概念和结构 1.1 栈的概念 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作&#xff0c;进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵循后进先出&#xff08;先进后出&#xff09;原则。特性与栈的…

杂七杂八-必备软件下载

必备软件下载 学术软件工作软件tips软件 仅个人笔记使用&#xff0c;后续持续更新&#xff0c;感谢点赞关注 学术软件 幕布&#xff1a;记录各种笔记&#xff0c;文本和思维导图快捷互换边界AIchat&#xff1a;集成了多个最新大模型工具&#xff0c;功能丰富&#xff0c;推荐使…

html加载页面

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>算数模一体化</title> </head><b…

GPT-4论文阅读

GPT-4 Technical Report论文阅读 文章目录 GPT-4 Technical Report论文阅读 Abstract训练的稳定性Training processPredictable scaling训练的稳定性多么难能可贵 Capabilities考试成绩传统的benchmark语言方面的能力Visual inputsSteerability LimitationsRisks & mitigat…

初识Linux · 进程(2)

目录 前言&#xff1a; 有关进程的相关理解 前言&#xff1a; 本文会开始慢慢切入进程了&#xff0c;当然&#xff0c;切入进程之前&#xff0c;我们需要再次复习一下操作系统&#xff0c;后面接着是介绍什么是进程&#xff0c;如何查看进程&#xff0c;在Linux中对应的文件…

你真的了解电阻吗

电阻通常有下面几种表示符号。 我们用的最多的还是定值电阻。 ESP32参考设计原理图用的是折线。 GD32中参考设计原理图用的是小方框。 包括我们画原理图的时候用的基本也是小方框。 折线符号属于ANSI&#xff08;美国标准&#xff09;矩形符号属于DIN标准&#xff08;德国工业…

Linux基本

一、安装 &#xff08;一&#xff09;bios basic input / output system cpu虚拟化技术需要开启 intel amd 不同品牌进入bios快捷键不一样 &#xff08;二&#xff09;vmware 新建 配置硬件 硬盘 建议单个虚拟硬盘文件&#xff0c;比较好管理 r如果有转移的需求&#xff…

freertos 任务调度—抢占式, 时间片

FreeRTOS 操作系统支持三种调度方式&#xff1a; 抢占式调度&#xff0c;时间片调度和合作式调度。 实际应用主要是抢占式调度和时间片调度&#xff0c;合作式调度用到的很少. 1,抢占式调度 每个任务都有不同的优先级&#xff0c; 任务会一直运行直到被高优先级任务抢占或者遇到…

使用 ShuffleNet 模型在 CIFAR-100 数据集上的图像分类

简介 在深度学习领域&#xff0c;图像分类任务是衡量算法性能的重要基准。本文将介绍我们如何使用一种高效的卷积神经网络架构——ShuffleNet&#xff0c;来处理 CIFAR-100 数据集上的图像分类问题。 CIFAR-100 数据集简介 CIFAR-100 数据集是一个广泛使用的图像分类数据集&…

使用了@Bean启动成功还能注入失败?秒级解决 定位分析

文章目录 Bean 断点跟不进去为什么需要多个同类型bean怎么友好处理同类型bean【任选一种】彩蛋 Bean 断点跟不进去 结论&#xff1a;你的其他代码 或者底层依赖&#xff0c;一定有改类型的自动注入代码&#xff0c;在Spring 机制中&#xff0c;默认拒绝Bean重写&#xff0c;你…

2024年一区SCI-极光优化算法 Polar Lights Optimization-附Matlab免费代码

引言 本期介绍了一种名为极光优化算法 Polar Lights Optimization (PLO)的元启发式算法。极光是一种独特的自然奇观&#xff0c;当来自太阳风的高能粒子在地磁场和地球大气层的影响下汇聚在地球两极时&#xff0c;就会发生极光。该成果于2024年8月最新发表在国际顶级JCR 1区、…

python画图|3D直方图基础教程

前述已经完成了直方图和3D图的基本学习&#xff0c;链接如下&#xff1a; 直方图&#xff1a;python画图|水平直方图绘制-CSDN博客 3D图&#xff1a;python画图|水平直方图绘制-CSDN博客 现在我们尝试把二者结合&#xff0c;画3D直方图。 【1】官网教程 首先&#xff0c;依…

微信开放标签【wx-open-launch-weapp】使用方法,亲测好用

如果你按照微信开放标签的文档来集成&#xff0c;那么恭喜你&#xff0c;绝对&#xff08;99%&#xff09;报错&#xff0c;今天在这里给大家演示一下在vue3中正确使用wx-open-launch-weapp的方法。 第一步&#xff1a; 配置wx.config&#xff0c;这个就不过多介绍了&#xff…

上海宝钢阿赛洛引领“绿能革命”:二期屋顶光伏项目赋能“双碳”目标新篇章

在“双碳”战略的宏伟蓝图下&#xff0c;一场能源革命的浪潮正席卷而来&#xff0c;分布式光伏以其独特的魅力成为这场变革中的璀璨明星。上海宝钢阿赛洛激光拼焊有限公司积极响应国家号召&#xff0c;携手上海宝钢节能环保技术有限公司&#xff0c;于近日宣布其屋顶光伏发电项…

SpringSecurity原理解析(五):HttpSecurity 类处理流程

1、SpringSecurity 在spring boot中与SSM项目中基于配置文件的区别 通过前边的笔记我们可以知道&#xff0c;在传统的SSM项目中 SpringSecurity的使用是基于配置文件 的&#xff0c;然后spring 容器初始化的时候将 SpringSecurity 中的各种标签解析成对应的Bean对象&#xff0c…

Cortex-M3架构学习:

异常类型 Cortex-M3 在内核水平上搭载了一个异常响应系统&#xff0c;支持为数众多的系统异常和外部中断。其 中&#xff0c;编号为 1 &#xff0d; 15 的对应系统异常&#xff0c;大于等于 16 的则全是外部中断。 Cortex-M3支持的中断源数目为 240 个&#xff0c;做成芯片后…

TensorFlow深度学习框架改进K-means、SOM自组织映射聚类算法及上海招生政策影响分析研究|附代码数据

全文链接&#xff1a;https://tecdat.cn/?p37652 原文出处&#xff1a;拓端数据部落公众号 分析师&#xff1a;Chen Zhang 在教育政策研究领域&#xff0c;准确评估政策对不同区域和学生群体的影响至关重要。2021 年上海市出台的《上海市初中学业水平考试实施办法》对招生…

PDF转Excel小达人养成记

在现代职场&#xff0c;数据管理与格式转换可谓是日常任务的重头戏&#xff1b;有时我们手头有一份PDF文件&#xff0c;但需要将其中的数据整理成Excel表格&#xff0c;这该如何是好&#xff1f;别急&#xff0c;今天我就来给大家介绍几款好用的PDF转Excel工具&#xff0c;以及…

使用您自己的图像微调 FLUX.1 LORA 并使用 Azure 机器学习进行部署

目录 介绍 了解 Flux.1 模型系列 什么是 Dreambooth&#xff1f; 先决条件 使用 Dreambooth 微调 Flux 的步骤 步骤 1&#xff1a;设置环境 第 2 步&#xff1a;加载库 步骤 3&#xff1a;准备数据集 3.1 通过 AML 数据资产&#xff08;URI 文件夹&#xff09;将图像上传到…