欢迎光临散文网 会员登陆 & 注册

Unity 手把手教你实现有向距离场(SDF)图像生成工具(2)【猴子都能学会】

2023-07-05 15:03 作者:今天摸鱼就好了  | 我要投稿

Unity 手把手教你实现有向距离场(SDF)图像生成工具(1)【猴子都能学会】 - 哔哩哔哩 (bilibili.com)

     在上一篇文章中我们完成了工具窗口的绘制, 现在就开始实现基本功能。

        首先我们需要判断贴图是否为空

    然后声明一个私有变量用来储存我们保存图像的路径

    接下来实现一个窗口来保存贴图

    这里直接调用unity的API就行了,再判断一下路径是否为空

现在我们点击按钮就会出现一个保存窗口了,但点了保存后依然什么也没有发生。

    现在我们再声明一个私有的ComputeShader,并把我们刚刚创建的ComputeShader给上去

    这里通过路径去加载我们的ComputeShader,所以注意这里路径是你ComputeShader所在的位置不要写错了

    现在我们再声明两个int类型的变量用来保存图像的宽度和高度。

然后我们把图像的宽度和高度赋值给sourcWidth和sourceHeight

     好了接下来我们再来写一个函数,该函数将读取贴图的边界条件,所以我们需要向该函数传入一张Texture2D。

    首先我们需要判断一下传入的图片是否为可读,使用该工具必须把传入的图像设置为可读才行,并且不能压缩,压缩会严重影响生成的SDF精度。

    还记得我们最开始创建了一个ComputeShader吗?我们至此还没有对它进行任何修改,现在让我们来打开它,你会看到如下代码,这就是unity为我们生成的ComputeShader的模板

    这里unity给我们写了注释,每个Kernel会和下面的功能相对应,我们可以在一个ComputeShader中写多个Kernel,他这里有个RWTexture2D<float4> Result; 这里的RW代表了RandomWrite(无序写入)的意思,我们接下来会利用ComputeShader,并行读取图像数据,所以数据的填充并没有先后顺序,如果我们只想读取图像,那么可以直接用Texture2D<float4>就可以了,[numthreads(8,8,1)]这个是我们多线程中非常重要的存在,它划分了我们的线程,[numthreads(x,y,z)]而其中x y z,并不是我们想写多少写多少想给多大给多大,我们需要满足x * y * z <= 1024; 而我们在ComputeShader中定义的线程和我们在C#中定义的线程组的乘积需要满足我们的线程总数。

    我在上面对于ComputeShader做了一下简单介绍,当然还要明白,我们输入输出的不仅仅是贴图还能是结构体,所以接下来我们会利用ComputeShader将图像的边界条件存储在一个结构体里面输出出来。

    接下来让我们看看如何输入一张图像,输出一个结构体。

    我们现在在ComputeShader中声明一个结构体,里面只有一个int 类型叫edge。

    再声明一个_Width,该变量由C#传入代表图像宽度,再需要一个传出的结构体RWStructuredBuffer<EdgePixel> Result;

    让我们回到C#,我们声明一个结构体与ComputeShader的结构体对应,两者除了名字必须完全一致

    接下来我们创建一个ComputeBuffer去实现GPU与CPU的数据交换

    我们基于上面对edgeBuffer赋值,并将数据传入ComputeShader参与计算

    现在再回到ComputeShader,这里我们需要把二值图的边界找出来

    现在我们返回C#中,我们需要再来一次并行计算将结构体的数组转换为一张图像

首先我们先声明一张RenderTexture来储存图像

    现在我们回到ComputeShader写另一个kernel来生成图像,这里非常简单,唯一注意的是现在我们的图像变成了输出的对象,需要用RWTexture2D<float4>

    现在我们再到C#中来执行这个Kernel生成图像

    现在图像被保存在rt0中了,现在我们需要输出一张png格式图片,所以我们先声明一个Texture2D作为目标图像

现在我们ComputeSDF()中去调用我们之前写的两个方法

    以上我们再来执行工具看看是不是能得到如下图像,如果可以说明之前的代码就成功了,距离的计算会在下一篇文章中讲解

图像边缘像素


Unity 手把手教你实现有向距离场(SDF)图像生成工具(2)【猴子都能学会】的评论 (共 条)

分享到微博请遵守国家法律