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

215 阅读2分钟

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

前言:

设一个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 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; float z=id.y;

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了。