Compute|Unity Compute Shader入门(大量对象随机赋值颜色实验)
文章目录
- 前言
-
- Compute Shader的简单介绍
- Compute Shader在Editor中
- Unity Compute Shader代码原理简介
- CPU对照组
- 运用Compute Shader解决问题
- 效果
前言 好久不见!今天使用Unity Compute Shader来实现一个大量正方体的随机颜色赋值,作为我的Unity ComputeShader入门练习。这个练习可以说很经典了。
Compute Shader的简单介绍 使用Compute Shader可以让GPU参与任意数据类型的运算,以此减小CPU的运算负荷。总所周知GPU十分擅长执行大量并行的简单算法,所以将此类运算交给GPU将能有效提升项目运行效率。Unity的Compute Shader使用的是HLSL书写。
你可以将Compute Shader当做C#脚本的一种延申,Compute Shader需要通过脚本来告诉它该什么时候执行以及如何执行。
Compute Shader在Editor中 你可以在Create -> Shader -> Compute Shader新建一个Compute Shader。
文章图片
Unity Compute Shader代码原理简介
// Each #kernel tells which function to compile;
you can have many kernels
#pragma kernel CSMain// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D Result;
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{// TODO: insert actual code here!Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0);
}
这就是Compute Shader默认代码,你在里面先声明Compute Shader运行时(从C#里面Dispatch时)要调用的函数(#pragma kernel CSMain),kernel可以有很多个。用于和C#脚本传递数据的参数(这里是RWTexture2D,要处理任意数据一般使用StructuredBuffer类型),以及函数本身的定义(在这里他产生了一堆float4的数据,其实就是一张图),函数参数括号里面的东西是线程的ID。
此外,你还需要调整numthreads里的参数,这个玩意指定了Compute Shader会生成的线程数,需要根据情况设置,具体的我也不懂了,先保持默认。
函数内部的运算要尽可能简单,GPU不能很好的执行分支操作,所以也不要在里面做if之类的操作。
如果你对细节感兴趣,链接这篇老外写的文章能解释更多。
Getting Started With Compute Shaders In Unity
知乎中文翻译版:
Unity3d | 浅谈 Compute Shader
简书大佬教程
Unity Compute Shader入门初探
CPU对照组 为了体现Compute Shader的作用,我们先实现一个单纯使用CPU的对照组方法。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
struct Cube
{public Vector3 position;
public Color color;
}public class ComputeShaderDemo : MonoBehaviour
{public int repetions;
public List objects;
public int count = 50;
private void Start()
{CreateCubes();
}private void CreateCubes()
{for (int i = 0;
i < count;
i++)
{for (int j = 0;
j < count;
j++)
{var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
go.transform.position = new Vector3(i, j, 0);
Color color = UnityEngine.Random.ColorHSV();
go.GetComponent().material.SetColor("_Color", color);
objects.Add(go);
go.transform.SetParent(this.transform);
}
}
}[ContextMenu("OnRandomize CPU")]
public void OnRandomizeCPU()
{for (int i = 0;
i < repetions;
i++)
{for (int c = 0;
c < objects.Count;
c++)
{GameObject obj = objects[c];
obj.transform.position = new Vector3(obj.transform.position.x, obj.transform.position.y, UnityEngine.Random.Range(-0.1f, 0.1f));
obj.GetComponent().material.SetColor("_Color", UnityEngine.Random.ColorHSV());
}
}
}
上述为C#脚本,干的事情就是给一堆正方体对象随机赋予颜色和y轴坐标,然后重复若干次,目的就是为了模拟需要大量运行的简单运算。
文章图片
在50 * 50个正方体,执行1000次时,我的CPU运行起来就已经相当卡了,10000次重复直接卡死。
运用Compute Shader解决问题 现在我们将运用Compute Shader调用GPU来进行运算。
#pragma kernel CSMainstruct Cube {float3 position;
float4 color;
};
// StructuredBuffer
RWStructuredBuffer cubes;
float repetions;
float resolution;
// GPU将用到的随机算法
float rand(float2 co)
{return(frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453)) * 1;
}[numthreads(10,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{float xPos = id.x / resolution;
Cube cube = cubes[id.x];
for (int i = 0;
i < repetions;
i++)
{float zPos = rand(float2(xPos, cube.position.z));
cube.position.z = zPos;
float r = rand(float2(cube.color.r, cube.color.g));
float g = rand(float2(cube.color.g, cube.color.b));
float b = rand(float2(cube.color.b, cube.color.r));
cube.color = float4(r, g, b, 1.0);
}
cubes[id.x] = cube;
}
这是用来做这件事的Compute Shader代码,基本上干的就是OnRandomizeCPU的事情,当然,数据要从C#脚本中传递给Compute Shader,Compute Shader拿数据做完运算后再将运算结果返回给C#脚本,脚本最后再运用这些结果。
[ContextMenu("OnRandomize GPU")]
public void OnRandomizeGPU()
{int colorSize = sizeof(float) * 4;
int vector3Size = sizeof(float) * 3;
int totalSize = colorSize + vector3Size;
// 这玩意将与Compute Shader中的StructuredBuffer绑定
ComputeBuffer cubeBuffer = new ComputeBuffer(data.Length, totalSize);
// 将需要处理的数据放到buffer里面
cubeBuffer.SetData(data);
// 将cubeBuffer与Compute Shader中的StructuredBuffer绑定(可以这么说吧)
// 现在cubeBuffer相当于CPU与GPU之间的输入输出缓冲区
computeShader.SetBuffer(0, "cubes", cubeBuffer);
computeShader.SetFloat("resolution", data.Length);
computeShader.SetFloat("repetions", repetions);
// 执行Compute Shader
computeShader.Dispatch(0, data.Length / 10, 1, 1);
// 从缓冲区获取数据
cubeBuffer.GetData(data);
// 运用数据
for (int i = 0;
i < objects.Count;
i++)
{GameObject obj = objects[i];
Cube cube = data[i];
obj.transform.position = cube.position;
obj.GetComponent().material.SetColor("_Color", cube.color);
}
cubeBuffer.Dispose();
}
}
这是C#脚本,干的就是我刚刚说的,传递数据给Compute Shader,运行Shader,拿到结果并将结果赋值给对象。
效果 【Compute|Unity Compute Shader入门(大量对象随机赋值颜色实验)】效果可以说非常好了,10000次重复都是秒完成,当次数到100万次时才出现了一点点延迟,Compute Shader的性能瓶颈往往来自数据传递过程。
推荐阅读
- Unity和Android通信系列文章2——扩展UnityPlayerActivity
- unity探究UGUI的Image中sprite和overrideSprite的区别
- unity|unity 在iOS平台跳转appstore
- 计算机与时间
- OpenCV|OpenCV for Unity 通过WebCamTextureToMatHelper帮助类来获取摄像头的画面
- Unity中使用反射机制调用函数
- Unity|Unity 对象池
- Artificial|Artificial Intelligence and Computer Vision Salon
- Shader基础笔记(2)-|Shader基础笔记(2)- 数学基础
- IOS打包流程-2打包ipa