本文已参与[新人创作礼]活动,一起开启掘金创作之路。
前言:
设一个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了。