Unity学习笔记:使用 Compute Shader 绘制贴图
Compute Shader 作为现在比较流行的技术,笔者作为美术转行的 TA,之前并没有怎么接触过,本着不学习就会被淘汰的原则,学习了一下 Compute Shader,顺便也和大家分下心得。Compute Shader 是什么,知乎上很多大佬已经分享过了,也就不用我这个菜鸡给大家介绍了,这里就分享下我用 Compute shader 采样并绘制贴图的心得,废话不多说,先上效果:
这是一个 Unity 自带的 cube 模型,贴图采样的是利用 Compute Shader 绘制的 256 * 256 像素的 Render Texture。
使用 Compute Shader 画贴图,首先需要安排一个可读写的缓存区,这里给到一个可读写的 Texture2D:
Compute Shader:
RWTexture2D<float4> Result;C#:
_tex = new RenderTexture(256, 256, 24); /* 设置 RenderTexture 尺寸 */
_tex.enableRandomWrite = true; /* 设置 RenderTexture 可写入 */
_tex.Create();
接着就要考虑到这个 Compute Shader 的 kernel 函数的线程数量,因为绘制目标是一个 256 *256 像素的正方形贴图,所以 numthread 也分配一块 2D 正方形数量的线程,并且要被 256 像素整除,原则上是:
ThreadGroupX * ThreadsX = RWTexture.width;
ThreadGroupY * ThreadsY = RWTexture.height;所以,在 Compute Shader 里,我们分配 8*8 个 Thread,在C# 里分配 32*32 个 ThreadGroup;
Compute Shader:
void CSMain (uint2 id : SV_DispatchThreadID)
{
// Do something
}C#:
......
ComputeShader.Dispatch(_kernelHandle, 32, 32, 1);
......
接下来就是在贴图上画图形了,如何在贴图指定的位置画上指定的颜色呢?比如:
float4 color = float4(1,1,1,1);/* 白色 */
Result = color;/* 表示在像素坐标(100,100)上画白色 */也就是说,我们只需要在 Resul 采样里告诉 GPU 画在什么像素坐标上,画什么颜色即可;
以上图右边的同心圆效果为例,根据半圆的公式:
我们在 256 * 256 (0,255)区间的画布中心点 c 上画一个直径 256 的整圆则是:
Compute Shadr:
float r = 127.5;
float r_squared = r * r;
uint upper_semicircle_y = uint(sqrt(r_squared-(x-127.5)*(x-127.5))+127.5); /* 计算上半圆的 y 坐标 */
uint lower_semicircle_y = uint(-sqrt(r_squared-(x-127.5)*(x-127.5))+127.5);/* 计算下半圆的 y 坐标 */
float4 color = float4(1,1,1,1); /* 白色 */
if(y == upper_semicircle_y || y == lower_semicircle_y)
Result = color; /* 根据圆的坐标对应采样,画白色 */结果如下:
画同心圆的画只要 for 循环多次即可:
完整 Compute shader:
#pragma kernel CSMain
RWTexture2D<float4> Result;
void CSMain (uint2 id : SV_DispatchThreadID)
{
uint x = id.x;
uint y = id.y;
float4 color0 = float4(1,1,1,1);
/* Draw circle */
float r = 40;
float r_squared = r * r;
for (int i = 0; i < 15; ++i)
{
uint upper_semicircle_y = uint(sqrt(r_squared-(x-127.5)*(x-127.5))+127.5);
uint lower_semicircle_y = uint(-sqrt(r_squared-(x-127.5)*(x-127.5))+127.5);
if(y == upper_semicircle_y)
Result = float4((float)x/256,(float)y/256,1,1);
else if(y == lower_semicircle_y)
Result = float4((float)x/256,(float)lower_semicircle_y/256,1,1);
r_squared += 2000;
}
/* Draw square */
{
bool drawSquare = 0;
uint2 edge = { uint2(0,y), uint2(255,y), uint2(x,0), uint2(x,255) };
for (int i = 0; i < 4; ++i)
{
if (x == edge.x && y == edge.y)
{
drawSquare = true;
break;
}
}
if (drawSquare)
Result = color0;
}
}完整 C#:
class DrawTex : MonoBehaviour
{
public ComputeShader ComputeShader;
public Material Mat;
RenderTexture _tex ;
private int _kernelID;
private void Awake()
{
_tex = new RenderTexture(256, 256, 24); /* 设置 RenderTexture 尺寸 */
_tex.enableRandomWrite = true; /* 设置 RenderTexture 可写入 */
_tex.Create();
_kernelID = ComputeShader.FindKernel(&#34;CSMain&#34;);
ComputeShader.SetTexture(_kernelID, &#34;Result&#34;, _tex);
ComputeShader.Dispatch(_kernelID, 32, 32, 1);
Mat.mainTexture = _tex; /* Compute shader 不能直接渲染
* 需要使用一个普通 shader 来接收 compute shader 处理的结果
* 这里使用的是一个双面渲染的 Unlit shader 材质 */
}
private void OnDestroy()
{
_tex.Release();
}
}
页:
[1]