找回密码
 立即注册
查看: 312|回复: 0

Unity学习笔记:使用 Compute Shader 绘制贴图

[复制链接]
发表于 2022-4-22 16:06 | 显示全部楼层 |阅读模式
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:
[numthreads(8,8,1)]
void CSMain (uint2 id : SV_DispatchThreadID)
{
    // Do something
}C#:
......
ComputeShader.Dispatch(_kernelHandle, 32, 32, 1);
......
接下来就是在贴图上画图形了,如何在贴图指定的位置画上指定的颜色呢?比如:
float4 color = float4(1,1,1,1);  /* 白色 */
Result[uint2(100,100)] = 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[id] = color;                                                      /* 根据圆的坐标对应采样,画白色 */结果如下:


画同心圆的画只要 for 循环多次即可:


完整 Compute shader:
#pragma kernel CSMain

RWTexture2D<float4> Result;

[numthreads(8,8,1)]
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[id] = float4((float)x/256,(float)y/256,1,1);
        else if(y == lower_semicircle_y)
            Result[id] = float4((float)x/256,(float)lower_semicircle_y/256,1,1);
        r_squared += 2000;
    }

    /* Draw square */
    {
        bool drawSquare = 0;
        uint2 edge[4] = { 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[id] = 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("CSMain");
        ComputeShader.SetTexture(_kernelID, "Result", _tex);
        ComputeShader.Dispatch(_kernelID, 32, 32, 1);
        
        Mat.mainTexture = _tex;                              /* Compute shader 不能直接渲染
                                                              * 需要使用一个普通 shader 来接收 compute shader 处理的结果
                                                              * 这里使用的是一个双面渲染的 Unlit shader 材质 */
    }

    private void OnDestroy()
    {
        _tex.Release();
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 20:44 , Processed in 0.089204 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表