Learn ComputeShader 03 Passing data to shader

news2024/10/2 20:23:01

这次我们想要在一个平面中生成随机运动的圆形。之前传递数据都是通过setInt,setVector等方法进行的,但是这些方法并不能一下传递大量数据,比如一个结构体数组,一个数据块。所以这次的主要内容就是通过buffer传递大量数据。

首先是绘制圆形的代码,参数为圆心和半径

具体原理参考下面连接 

RasterisingLinesCircles.pdf (sunshine2k.de)

void plot1( int x, int y, int2 centre){
    Result[uint2(centre.x + x, centre.y + y)] = circleColor;
}

void plot8( int x, int y, int2 centre ) {
	plot1(  x,  y, centre );  plot1(  y,  x, centre );
	plot1(  x, -y, centre );  plot1(  y, -x, centre );
	plot1( -x, -y, centre );  plot1( -y, -x, centre );
	plot1( -x,  y, centre );  plot1( -y,  x, centre );
}

void drawCircle( int2 centre, int radius ) {
	int x = 0;
	int y = radius;
	int d = 1 - radius;

	while (x < y){
		if (d < 0){
			d += 2 * x + 3;
		}else{
			d += 2 * (x - y) + 5;
			y--;
		}
		
		plot8(x, y, centre);

		x++;
	}
}

接着先要绘制随机分布的静态的圆形。

先定义两个随机函数用来随机半径和圆心,

float random(float value, float seed = 0.546){
	float random = (frac(sin(value + seed) * 143758.5453));// + 1.0)/2.0;
	return random;
}

float2 random2(float value){
	return float2(
		random(value, 3.9812),
		random(value, 7.1536)
	);
}

接着编写核函数。

需要设置圆圈以及背景的颜色,要在同一个脚本中同时实现背景以及圆圈的绘制,需要编写两个核函数,并且先调用背景核函数,然后调用圆圈核函数覆盖背景颜色,这里两个核函数共用一张共享纹理。

第一个核函数每个线程随机绘制一个圆形,一个线程组有32个线程。同时为了保证圆圈在随机运动,又加了时间对圆心的影响

[numthreads(32,1,1)]
void Circles (uint3 id : SV_DispatchThreadID)
{
	int2 centre = (int2)(random2((float)id.x+time)*(float)texResolution);
	int radius = (int)(random((float)id.x)*30.0);
	drawCircle( centre, radius );
}

[numthreads(8,8,1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
	Result[id.xy]=clearColor;
}

在代码中先启用设置背景颜色的核函数,接着启用绘制圆圈的核函数。这里在x方向上分配了十个线程组,所以会绘制出320个圆形

    private void DispatchKernels(int count)
    {
        shader.Dispatch(clearHandle, texResolution / 8, texResolution / 8, 1);
        shader.SetFloat("time", Time.time);
        shader.Dispatch(circlesHandle, count, 1, 1);
    }

    void Update()
    {
        DispatchKernels(10);
    }

下面是输出结果

 目前圆圈的运动难以控制,所以接下来我们要使用buffer来控制圆圈的运动。

下面是使用buffer的主要步骤

首先要做的是创建一个结构体来存储数据的单个实例,这里我们主要想实现控制圆的运动并将圆转回原点.,circledata表示所有圆圈的数据,buffer用于在 GPU 中存储和操作这些圆圈的数据。

struct Circle
    {
        public Vector2 origin;
        public Vector2 velocity;
        public float radius;
    }

    int count = 10;
    Circle[] circleData;
    ComputeBuffer buffer;

接下来要初始化数据,分别设置每个圆形的圆心,半径,移动速度,其中Random.value 会返回一个0-1之间的值, shader.GetKernelThreadGroupSizes(circlesHandle, out threadGroupSizeX, out _, out _);这个方法会获取我们在shader代码中设置的线程组在x方向上的值并输出到threadGroupSizeX这个变量

    void InitData()
    {
        circlesHandle = shader.FindKernel("Circles");

        uint threadGroupSizeX;

        shader.GetKernelThreadGroupSizes(circlesHandle, out threadGroupSizeX, out _, out _);
        int total = (int)threadGroupSizeX * count;
        circleData = new Circle[total];

        float speed = 100;
        float halfSpeed = speed * 0.5f;
        float minRadius = 10.0f;
        float maxRadius = 30.0f;
        float radiusRange = maxRadius- minRadius;


        for (int i = 0; i < total; i++)
        {
            Circle circle = circleData[i];
            circle.origin.x = Random.value * texResolution;
            circle.origin.y = Random.value * texResolution;
            circle.velocity.x = (Random.value * speed) - halfSpeed;
            circle.velocity.y = (Random.value * speed) - halfSpeed;
            circle.radius = Random.value * radiusRange + minRadius;
            circleData[i] = circle;
        }


    }

在传递数据之前,还需要在shader代码中设置接收数据的缓冲区

struct circle{
	float2 origin;
	float2 velocity;
	float radius ;
};

StructuredBuffer<circle> circlesBuffer;

接下来回到脚本代码中准备传递buffer到shader中.

第一行代码计算出单个结构体的大小

下面是创建buffer的构造函数,具体参数以及含义如下图

然后使用数据填充buffer,最后将缓冲区传递给着色器

 int stride = (2 + 2 + 1) * sizeof(float);
        buffer = new ComputeBuffer(circleData.Length, stride);
        buffer.SetData(circleData);
        shader.SetBuffer(circlesHandle, "circlesBuffer", buffer);

接下来将原来核函数的值替换成buffer的值

[numthreads(32,1,1)]
void Circles (uint3 id : SV_DispatchThreadID)
{
	int2 centre = (int2)(circlesBuffer[id.x].origin+circlesBuffer[id.x].velocity*time);
	int radius = (int)(circlesBuffer[id.x].radius);
	drawCircle( centre, radius );
}

目前所有圆圈的运动平滑了很多,但是还有一个问题就是最终所有的圆圈都会飞出屏幕。要解决这个问题也很简单,只需要当圆圈跑到屏幕外的时候,将圆心重新设置到屏幕内

[numthreads(32,1,1)]
void Circles (uint3 id : SV_DispatchThreadID)
{
	int2 centre = (int2)(circlesBuffer[id.x].origin+circlesBuffer[id.x].velocity*time);
	int radius = (int)(circlesBuffer[id.x].radius);

	while(centre.x>texResolution) centre.x-=texResolution;
	while(centre.x<0) centre.x+=texResolution;
	while(centre.y>texResolution) centre.y-=texResolution;
	while(centre.y<0) centre.y+=texResolution;

	drawCircle( centre, radius );
}

总结:使用computer buffer 的步骤

完整代码:

PassData.cs

using UnityEngine;
using System.Collections;

public class PassData : MonoBehaviour
{

    public ComputeShader shader;
    public int texResolution = 1024;

    Renderer rend;
    RenderTexture outputTexture;

    int circlesHandle;
    int clearHandle;

    struct Circle
    {
        public Vector2 origin;
        public Vector2 velocity;
        public float radius;
    }

    int count = 10;
    Circle[] circleData;
    ComputeBuffer buffer;

    public Color clearColor = new Color();
    public Color circleColor = new Color();

    // Use this for initialization
    void Start()
    {
        outputTexture = new RenderTexture(texResolution, texResolution, 0);
        outputTexture.enableRandomWrite = true;
        outputTexture.Create();

        rend = GetComponent<Renderer>();
        rend.enabled = true;

        InitData();

        InitShader();
    }

    void InitData()
    {
        circlesHandle = shader.FindKernel("Circles");

        uint threadGroupSizeX;

        shader.GetKernelThreadGroupSizes(circlesHandle, out threadGroupSizeX, out _, out _);
        int total = (int)threadGroupSizeX * count;
        circleData = new Circle[total];

        float speed = 100;
        float halfSpeed = speed * 0.5f;
        float minRadius = 10.0f;
        float maxRadius = 30.0f;
        float radiusRange = maxRadius- minRadius;


        for (int i = 0; i < total; i++)
        {
            Circle circle = circleData[i];
            circle.origin.x = Random.value * texResolution;
            circle.origin.y = Random.value * texResolution;
            circle.velocity.x = (Random.value * speed) - halfSpeed;
            circle.velocity.y = (Random.value * speed) - halfSpeed;
            circle.radius = Random.value * radiusRange + minRadius;
            circleData[i] = circle;
        }


    }

    private void InitShader()
    {

        clearHandle = shader.FindKernel("Clear");

        shader.SetInt( "texResolution", texResolution);
        shader.SetTexture( circlesHandle, "Result", outputTexture);
        shader.SetTexture(clearHandle, "Result", outputTexture);

        shader.SetVector("clearColor", clearColor);
        shader.SetVector("circleColor", circleColor);

        int stride = (2 + 2 + 1) * sizeof(float);
        buffer = new ComputeBuffer(circleData.Length, stride);
        buffer.SetData(circleData);
        shader.SetBuffer(circlesHandle, "circlesBuffer", buffer);


        rend.material.SetTexture("_MainTex", outputTexture);
    }
 
    private void DispatchKernels(int count)
    {
        shader.Dispatch(clearHandle, texResolution / 8, texResolution / 8, 1);
        shader.SetFloat("time", Time.time);
        shader.Dispatch(circlesHandle, count, 1, 1);
    }

    void Update()
    {
        DispatchKernels(10);
    }
}

PassData.compute

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel Circles
#pragma kernel Clear

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Result;

int texResolution;

float4 clearColor ;
float4 circleColor;
float time;

struct circle{
	float2 origin;
	float2 velocity;
	float radius ;
};

StructuredBuffer<circle> circlesBuffer;

/*Returns pseudo random number in range 0 <= x < 1 */
float random(float value, float seed = 0.546){
	float random = (frac(sin(value + seed) * 143758.5453));// + 1.0)/2.0;
	return random;
}

float2 random2(float value){
	return float2(
		random(value, 3.9812),
		random(value, 7.1536)
	);
}

void plot1( int x, int y, int2 centre){
    Result[uint2(centre.x + x, centre.y + y)] = circleColor;
}

void plot8( int x, int y, int2 centre ) {
	plot1(  x,  y, centre );  plot1(  y,  x, centre );
	plot1(  x, -y, centre );  plot1(  y, -x, centre );
	plot1( -x, -y, centre );  plot1( -y, -x, centre );
	plot1( -x,  y, centre );  plot1( -y,  x, centre );
}

void drawCircle( int2 centre, int radius ) {
	int x = 0;
	int y = radius;
	int d = 1 - radius;

	while (x < y){
		if (d < 0){
			d += 2 * x + 3;
		}else{
			d += 2 * (x - y) + 5;
			y--;
		}
		
		plot8(x, y, centre);

		x++;
	}
}

[numthreads(32,1,1)]
void Circles (uint3 id : SV_DispatchThreadID)
{
	int2 centre = (int2)(circlesBuffer[id.x].origin+circlesBuffer[id.x].velocity*time);
	int radius = (int)(circlesBuffer[id.x].radius);

	while(centre.x>texResolution) centre.x-=texResolution;
	while(centre.x<0) centre.x+=texResolution;
	while(centre.y>texResolution) centre.y-=texResolution;
	while(centre.y<0) centre.y+=texResolution;

	drawCircle( centre, radius );
}

[numthreads(8,8,1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
	Result[id.xy]=clearColor;
}


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

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

相关文章

Android 本地化、多语言切换:Localization

目录 1&#xff09;如何实现多语言切换、如何实现跟随手机语言切换而切换app语言 2&#xff09;Localization是什么 3&#xff09;不管手机语言如何&#xff0c;根据用户在App选择的语言&#xff0c;只切换App语言 4&#xff09;文字长短不一样&#xff0c;怎么办呢? 一、Lo…

积分的简介

积分的简介 集成是一种添加切片以找到整体的方法。积分可用于查找区域、体积、中心点和许多有用的东西。但是&#xff0c;最简单的方法是从找到函数和 x 轴之间的区域开始&#xff0c;如下所示&#xff1a; 1.面积是什么&#xff1f;是片 我们可以在几个点上计算函数&#xf…

Error in importing environment OpenAI Gym

题意&#xff1a;尝试导入OpenAI Gym库中的某个环境时发生了错误 问题背景&#xff1a; I am trying to run an OpenAI Gym environment however I get the following error: 我正在尝试运行一个OpenAI Gym环境&#xff0c;但是我遇到了以下错误&#xff1a; import gym env…

Spring Boot整合MyBatis-Flex

说明&#xff1a;MyBatis-Flex&#xff08;官网地址&#xff1a;https://mybatis-flex.com/&#xff09;&#xff0c;是一款数据访问层框架&#xff0c;可实现项目中对数据库的访问&#xff0c;类比MyBatis-Plus。本文介绍&#xff0c;在Spring Boot项目整合MyBatis-Flex。 创…

专业解析:U盘打不开的应对与数据恢复策略

一、U盘打不开的困境解析 在日常的数据存储与传输中&#xff0c;U盘作为便携的存储媒介&#xff0c;其重要性不言而喻。然而&#xff0c;当您急需使用U盘时&#xff0c;却遭遇“U盘打不开”的尴尬境地&#xff0c;这无疑会给工作和学习带来极大的不便。U盘打不开的原因多种多样…

Javase--Date

1.Date简介 Date的学习: 1. java.util包下的类 2.用于日期、时间的描述 3. 实际上时距离一个固定时间点1970年1月1日00:00:00的毫秒数 4.我们常用的是格林威治时间:GMT UTC:世界调整时间 5.固定时间点:说的其实是本初子午线的时间。因此北京时间是1970年1月1日8:00:…

评估生成分子/对接分子的物理合理性工具 PoseBusters 评测

最近在一些分子生成或者对接模型中&#xff0c;出现了新的评估方法 PoseBusters&#xff0c;用于评估生成的分子或者对接的分子是否符合化学有效性和物理合理性。以往的分子生成&#xff0c;经常以生成分子的有效性、新颖性、化学空间分布&#xff0c;与口袋的结合力等方面进行…

.NET反混淆神器de4dot使用介绍

最近在逛看雪时&#xff0c;发现一个帖子&#xff0c;[原创]常见语言基础逆向方法合集-软件逆向-看雪-安全社区|安全招聘|kanxue.com。里面介绍 了常见语言基础逆向方法合集。关于.net程序逆向这块&#xff0c;介绍了三个工具。 .NET Reflector .NET Decompiler: Decompile A…

C++中string类常用函数的用法介绍

在C中&#xff0c;string是一个功能强大的类&#xff0c;用于处理和操作文本数据。它属于C标准库中的字符串库部分&#xff0c;专门用于处理字符串。与传统的C风格字符串相比&#xff0c;它提供了动态内存管理、类型安全和丰富的操作方法。 目录 一、构造和初始化 二、获取字…

算法训练,项目

一.木材加工 题解&#xff1a; 二分答案&#xff0c;左边0&#xff0c;右边可以为最长的木头&#xff0c;但我直接赋值了一个很大的值&#xff0c;进行二分&#xff0c;随后写个check;内部遍历木头截取为mid木块的个数&#xff0c;要是>k&#xff0c;满足要求&#xff0c;还…

【时时三省】(C语言基础)一维数组

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ——csdn时时三省 数组 数组就是一组数 数组的官方定义是一组相同类型元素的集合 一堆数组的创建和初始化 求组的创建 数组是一组相同类型元素的集合。数组的创建当时是: type&#xff3f;t arr&#x…

【JavaEE】定时器

目录 前言 什么是定时器 如何使用java中的定时器 实现计时器 实现MyTimeTask类 Time类中存储任务的数据结构 实现Timer中的schedule方法 实现MyTimer中的构造方法 处理构造方法中出现的线程安全问题 完整代码 考虑在限时等待wait中能否用sleep替换 能否用PriorityBlo…

Linux网络——深入理解 epoll

目录 一、epoll 模型 1.1 前导知识 1.1.1 宏 offsetof 1.1.2 手动计算 1.2 epoll 模型 二、 epoll 工作模式 2.1 水平触发 特点&#xff1a; 2.2 边缘触发 特点&#xff1a; 边缘触发模式中的循环读取 结合非阻塞模式的优势 一、epoll 模型 经过了之前的学习&#…

什么是容器查询?分享 1 段优质 CSS 代码片段!

本内容首发于工粽号&#xff1a;程序员大澈&#xff0c;每日分享一段优质代码片段&#xff0c;欢迎关注和投稿&#xff01; 大家好&#xff0c;我是大澈&#xff01; 本文约 700 字&#xff0c;整篇阅读约需 1 分钟。 今天分享一段优质 CSS 代码片段&#xff0c;使用容器查询…

【算法设计题】实现以字符串形式输入的简单表达式求值,第2题(C/C++)

目录 第2题 实现以字符串形式输入的简单表达式求值 得分点&#xff08;必背&#xff09; 题解 1. 初始化和变量定义 2. 获取第一个数字并存入队列 3. 遍历表达式字符串&#xff0c;处理运算符和数字 4. 初始化 count 并处理加减法运算 代码详解 &#x1f308; 嗨&#xf…

你还在为PDF文件烦恼吗?试试这四款合并工具吧!

每天应对工作都是一个头两个大的&#xff0c;其中pdf的文件问题就是恼人的工作量之一了&#xff0c;这几年的工作经历下来也找了各种可以帮助解决PDF文件问题的工具&#xff0c;好在使用了一些助力我高效工作的软件&#xff0c;今天针对其中遇到的解决pdf合并问题的四款宝藏工具…

当Vercel的域名验证规则碰上JPDirect这种不配合的同学把我的脑袋擦出了火星子

文章目录 前言问题简单说明Vercel主要功能和特点 JPDirectNameServers解决方案 总结 前言 处理域名转移这件事已经过去好几天&#xff0c;终于抽出点时间来总结一下&#xff0c;解决这件事大概花了2周多时间&#xff0c;因为时差的原因导致沟通缓慢&#xff0c;今天准备长话短…

【leetcode】平衡二叉树、对称二叉树、二叉树的层序遍历(广度优先遍历)(详解)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

Zookeeper未授权访问漏洞

Zookeeper是分布式协同管理工具&#xff0c;常用来管理系统配置信息&#xff0c;提供分布式协同服务。Zookeeper的默认开放端口是2181。Zookeeper安装部署之后默认情况下不需要任何身份验证&#xff0c;造成攻击者可以远程利用Zookeeper&#xff0c;通过服务器收集敏感信息或者…

通信原理实验——PCM编译码

PCM编译码 实验目的 理解PCM编译码原理及PCM编译码性能熟悉PCM编译码专用集成芯片的功能和使用方法及各种时钟关系熟悉语音数字化技术的主要指标及测量方法 主要仪器设备及软件 硬件&#xff1a;多功能实验箱、示波器、导线 软件&#xff1a;无 实验原理 1. 抽样信号的量…