IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 【Unity】【ComputeShader】使用ComputeShader计算相机视野 -> 正文阅读

[游戏开发]【Unity】【ComputeShader】使用ComputeShader计算相机视野

前言:

? ? ? ? 设一个1024*1024的地块,需要实时计算有哪些地块在相机范围内。为了提高运行效率,这里用ComputeShader来计算。

1、准备工作

? ? ? ? 先在Unity右键创建一个ComputeShader,然后再创建一个Canvas,放一张RawImage(用于接受调试用的图片)。

? ? ? ? 然后创建一个控制类,添加以下控制组件:

public class ComputeTest : MonoBehaviour
{
    #region 外部赋值属性

    /// <summary>
    /// 用于显示用的调试图片;
    /// </summary>
    public RawImage ShowImage;

    /// <summary>
    /// 当前计算Shader
    /// </summary>
    public ComputeShader shader;

    /// <summary>
    /// 当前主相机
    /// </summary>
    public Camera mainCamera;

    #endregion
}

2、写ComputeShader代码:

? ? ? ? 原理就是,由客户端传过来摄像机的VP矩阵,然后在Shader里计算当前格子是否显示。然后写入到目标图片里就可以了。

? ? ? ? (具体的ComputeShader的介绍网上很多了,这里就不赘述了)

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

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

float4x4 WorldToCameraMatrix;

//使用VP矩阵计算是否可见
bool IsVisableInCamera (float x,float z)
{
    float4 clipPos =mul(WorldToCameraMatrix, float4(x,0,z,1));
    float3 ndcPos = float3(clipPos.x / clipPos.w, clipPos.y / clipPos.w, clipPos.z / clipPos.w);

    float view_x = 0.5f + 0.5f * clipPos.x / clipPos.w;
    float view_y = 0.5f + 0.5f * clipPos.y / clipPos.w;

    return view_x>=0 && view_x<=1 && view_y>=0 && view_y<=1 && clipPos.w>0;
}

[numthreads(8,8,1)]
void WolrdMapVisable (uint3 id : SV_DispatchThreadID)
{
    float x=id.x;//0~1024
    float z=id.y;//0~1024

    bool IsVisialbe = IsVisableInCamera(x,z);

    float retValue = IsVisialbe?1:0;    
    //如果可见显示白色,不可见显示黑色
    half4 col=half4(retValue,retValue,retValue,1);

    Result[id.xy] =col;
}

? ? ? ? 这里的图片是外部传过来,定好的大小(1024*1024);

3、编写Unity C#控制代码


    private const int TextureSize = 1024;//整个地图大小
    private const int ThreadGroup = 8;
    private const int ThreadGroupSize = TextureSize / ThreadGroup;

    //各种属性名;
    private const string ComputeShaderName = "WolrdMapVisable";
    private const string ComputeTextureName = "Result";
    private const string ComputeMatrixName = "WorldToCameraMatrix";

    private int kID;
    private int matixNameID;
    private RenderTexture texture;

    // Start is called before the first frame update
    void Start() { RunComputeShader(); }

    private void RunComputeShader()
    {
        //设置用于ComputeShader的贴图;
        texture = new RenderTexture(TextureSize, TextureSize, 24);
        texture.enableRandomWrite = true;
        texture.filterMode = FilterMode.Point;
        texture.Create();

        //将图片显示在UI上,调试用;
        ShowImage.texture = texture;

        //获取Shader的一些属性;
        kID = shader.FindKernel(ComputeShaderName);
        matixNameID = Shader.PropertyToID(ComputeMatrixName);

        //启动Shader:
        shader.SetTexture(kID, ComputeTextureName, texture);
        UpdateComputeShader();
    }

    void Update() { UpdateComputeShader(); }

    /// <summary>
    /// 每帧调用一次,设置相机矩阵
    /// </summary>
    private void UpdateComputeShader()
    {
        shader.SetMatrix(matixNameID, mainCamera.projectionMatrix * mainCamera.worldToCameraMatrix);
        shader.Dispatch(kID, ThreadGroupSize, ThreadGroupSize, 1);
    }

? ? ? ? 这个C#代码就比较简单了,简单看一看就OK了。

? ? ? ? 之后运行起来看看:

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

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:

? ? ? ? 通过ComputeBuffer读取其实有一定的性能消耗,如果数据太大则需要考虑一下是否合算。

?

?

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-09-12 13:27:58  更:2021-09-12 13:28:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 11:29:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码