在做草海剔除的时候,需要用到computeShader,此处先记录一下,后面整个分析使用DrawInstanceIndirect
(1)ComputeBuffer 的初始化:
var size = sizeof(float) * 10
上图中的 IndirectArguments的buffer类型, 在使用Graphics.DrawMeshInstancedIndirect这个接口时 必须声明为ComputeBufferType.IndirectArguments
(2)Dispatch
先numthreads定义好每个核函数对应线程组里线程的数量(tXtYtZ),再用Dispatch定义用多少线程组(gXgYgZ)来处理这个核函数
(3)将computeShader中的计算结果,作为参数,以buffer的形式传递给其它的shader使用(在其它shader中取值的时候用 instanceId作为索引)
不仅仅可以是 instanceId : SV_InstanceID, 还可以是 其它的类型
(4) Kernel函数中的参数:
(5)特别重要的:
在使用computeShader做计算赛选数据的时候,
#pragma kernel CSMain
struct PBuffer
{
float life;
float3 pos;
float3 scale;
float3 eulerAngle;
};
RWStructuredBuffer<PBuffer> buffer;
float deltaTime;
[numthreads(2,2,1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
int index = id.x + id.y * 2 * 2;
buffer[index].life = -deltaTime;
buffer[index].pos = buffer[index].pos + float3(0,deltaTime,0);
buffer[index].scale = buffer[index].scale;
buffer[index].eulerAngle = buffer[index].eulerAngle + float3(0,20 * deltaTime,0);
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using Random = UnityEngine.Random;
struct PBuffer
{
// size = 40;
public float life;
public Vector3 pos;
public Vector3 scale;
public Vector3 eulerAngle;
}
public class MyCompute : MonoBehaviour
{
public ComputeShader shader;
//public GameObject prefab;
public ComputeBuffer buffer;
private void Start()
{
CreatBuffer();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Dispatch();
}
if (Input.GetKeyDown(KeyCode.A))
{
GetComputeRes();
}
}
private int count = 20;
void CreatBuffer()
{
buffer = new ComputeBuffer(count,40);
PBuffer[] values = new PBuffer[count];
for (int i = 0; i < count; i++)
{
PBuffer m = new PBuffer();
InitStruct(ref m);
values[i] = m;
}
buffer.SetData(values);
}
void InitStruct(ref PBuffer pBuffer)
{
pBuffer.life = Random.Range(1f, 3f);
pBuffer.pos = Random.insideUnitSphere * 5f;
pBuffer.scale = Vector3.one * Random.Range(0.3f, 1f);
pBuffer.eulerAngle = new Vector3(0,Random.Range(0f,180f),0);
}
void Dispatch()
{
shader.SetFloat("deltaTime",Time.deltaTime);
int kid = shader.FindKernel("CSMain");
shader.SetBuffer(kid,"buffer",buffer);
shader.Dispatch (kid, 2, 2, 1);
}
void GetComputeRes()
{
PBuffer[] values = new PBuffer[count];
buffer.GetData(values);
}
void ReleaseBuffer()
{
buffer.Release();
}
private void OnDisable()
{
ReleaseBuffer();
}
}
关于如何使用CopyCount和取出筛选出的值:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ComputeShaderTest : MonoBehaviour
{
public ComputeShader computeShader;
void Awake()
{
//reference: https://answers.unity.com/questions/1035132/how-can-i-read-in-the-actual-elements-from-an-appe.html
var kernelID = computeShader.FindKernel("CSMain");
var appendBuffer = new ComputeBuffer(64, sizeof(int), ComputeBufferType.Append);
appendBuffer.SetCounterValue(0);
//定义了缓冲区长度为64*结构大小的appendBuffer.
var consumeBuffer = new ComputeBuffer(64, sizeof(int), ComputeBufferType.Append);
consumeBuffer.SetCounterValue(0);
consumeBuffer.SetData(new int[] { 97, 98, 99 });
consumeBuffer.SetCounterValue(3);
//consume类型结构相当于栈,所以取到的第一个值是99。
computeShader.SetBuffer(kernelID, "appendBuffer", appendBuffer);
computeShader.SetBuffer(kernelID, "consumeBuffer", consumeBuffer);
computeShader.Dispatch(kernelID, 1, 1, 1);
//单个线程组的大小为8,定义了1个线程组。也就是说会返回8个数据。
var countBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.IndirectArguments);
ComputeBuffer.CopyCount(appendBuffer, countBuffer, 0);
//通过这个方法拿到第一个数据。
int[] counter = new int[1] { 0 };
countBuffer.GetData(counter);
int count = counter[0];
Debug.Log("count: " + count);
var data = new int[count];
appendBuffer.GetData(data);
Debug.Log("data length: " + data.Length);
for (int i = 0; i < data.Length; i++)
{
Debug.Log(data[i]);
}
consumeBuffer.Release();
consumeBuffer.Dispose();
appendBuffer.Release();
appendBuffer.Dispose();
countBuffer.Release();
countBuffer.Dispose();
}
}
#pragma kernel CSMain
AppendStructuredBuffer<int> appendBuffer;
ConsumeStructuredBuffer<int> consumeBuffer;
[numthreads(8,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
if (id.x == 2)
{
int value = consumeBuffer.Consume();
appendBuffer.Append(value);
}
if (id.x > 2)
{
appendBuffer.Append(id.x);
}
}