【Unity3D】使用ComputeShader计算相机视野 (二)

382 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

书接上文,我们已经成功使用 ComputeShader 计算了视野,先运行起来看看效果:

image.png

image.png

可以看到可视范围(白色)就随着相机的变化而改变了。

4、将计算结果返回

然而,以上通过ComputeShader计算的结果仅仅是一张图片而已,C# 里面并不能获取到具体的值。在某些情况下(如显示小地图),这种情况就已经OK了。但如果我们要知道哪些坐标点是在相机范围内的,这个就需要使用ComputeBuffer来处理了。

在C#代码里做以下补充:


……
 
    private const string ComputeBufferName = "VisableCellBuffer";
    private int ComputeBufferID;
    private ComputeBuffer AppendBuffer;
 
……
 
private void RunComputeShader()
{
……
        //这里的初始化 AppendBuffer 的时候就要传入理论最大值,不然后续读取的时候会失败;
        AppendBuffer = new ComputeBuffer(TextureSize * TextureSize, sizeof(int), ComputeBufferType.Append);
        ComputeBufferID = Shader.PropertyToID(ComputeBufferName);
……
}
 
private void UpdateComputeShader()
{
……
        SetAppedBuffer();
……
}
 
private void SetAppedBuffer()
{
        AppendBuffer.SetCounterValue(0);
        shader.SetBuffer(kID, ComputeBufferID, AppendBuffer);
}

在ComputeShader里需要做以下补充:


……
 
AppendStructuredBuffer<int2> VisableCellBuffer;
 
……
 
[numthreads(8,8,1)]
void WolrdMapVisable (uint3 id : SV_DispatchThreadID)
{
……
 
    if(IsVisialbe)
    {
        //把可见的格子转换成int值,然后存进VisableCellBuffer里面。
        int2 index=x*10000+z;
        VisableCellBuffer.Append(index);
    }
 
……

这样就可以把显示个格子转成一个坐标ID传出来了。

 然后读取方法如下:

private int[] ArrRet = new int[ThreadGroupSize];
 
    private void ReadAppendBuffer()
    {
        var countBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.IndirectArguments);
        ComputeBuffer.CopyCount(AppendBuffer, countBuffer, 0);
 
        //通过这个方法拿到第一个数据,就是AppendBuffer的数量
        int[] counter = new int[1] { 0 };//ToDo,这里还可以继续优化;
        countBuffer.GetData(counter);
 
        int leftCount = counter[0];
        int startIndex = 0;
 
        while (leftCount > 0)
        {
            int leftSize = Mathf.Min(leftCount, ThreadGroupSize);
            AppendBuffer.GetData(ArrRet, 0, startIndex, leftSize);//分几次读取出来;
 
            for (int i = 0; i < ThreadGroupSize; i++)
            {
                //Debug.Log($"[{startIndex}|{i}] {ArrRet[i]}");
            }
 
            leftCount = leftCount - ThreadGroupSize;
            startIndex = startIndex + ThreadGroupSize;
        }
    }

这样就可以读取ComputeShader的计算结果了。

PS :

上面的读取方法,其实是做了ComputeShader里面最忌讳的一件事之一:GPU的运算数据下行。这个读取数据的效率是很低的,低到可以计划将效率拉回到传统方法。 所以到一定要慎用! 其实也有不下行的方法,但不在本文的讨论范围之内。