[MetalKit]16-Using-MetalKit-part-10使用MetalKit10

本系列文章是对 metalkit.org 上面MetalKit内容的全面翻译和学习.c++

MetalKit系统文章目录git


今天咱们关注Metal function中没用过的类型,kernel function内核函数compute shader计算着色器.你将常常听到它们两个的混合词变形词.内核是用于计算任务,也就是在GPU上进行大规模并行计算.例如:图像处理,科学模拟,等等.关于内核有一些重要特色:没有渲染管线,函数老是返回void,而且名字老是以kernel关键字开头,就像咱们之前用过的前面带有vertexvertex关键字的函数同样.github

让咱们从第8部分Part 8的playground处继续.首先,删除MathUtils.swift由于咱们已经不须要了.而后,在MetalView.swift中删除createBuffers()函数及其在初始化中的调用,还有两个缓冲器.将MTLRenderPipelineState声明替换为MTLComputePipelineState声明.下一步,来到registerShaders()函数.下面是新旧两个版本的不一样:swift

chapter10_1.png

注意,咱们不在使用descriptor了,而是在内核函数中直接建立MTLComputePipelineState.下一步,咱们看看drawRect()函数的不一样:ide

chapter10_2.png

注意,currentRenderPassDescriptor也不用了.命令编码器则用computeCommandEncoder()函数来建立.显然,咱们也再也不须要设置顶点缓冲器和绘制基本体了.做为替代,使用用一个设置了纹理的内核函数,建立线程组并指派它们干活.咱们用MTLSize来设置线程组的维数,及每次计算调用中要执行的线程组的数量.函数

最后,咱们到Shaders.metal文件中,用下面代码替换全部内容:post

#include <metal_stdlib>

using namespace metal;

kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    uint2 gid [[thread_position_in_grid]])
{
    output.write(float4(0, 0.5, 0.5, 1), gid);
}
复制代码

咱们只简单地给纹理中的每一个像素/位置设置了相同的颜色.如今若是你到playground的主页面,并显示Assistant editor中的Timeline,你应该能看到相似的视图:学习

chapter10_3.png

若是你看到了上面的输出,就说明准备好继续下去了.从当前开始,咱们将再也不关注主代码(MetalView.swift)了,由于咱们全部的工做都将在内核着色器中完成.ui

好了,让咱们先从简单的开始.用下面的代码替换内核函数中的代码:编码

int width = output.get_width();
int height = output.get_height();
float red = float(gid.x) / float(width);
float green = float(gid.y) / float(height);
output.write(float4(red, green, 0, 1), gid);
复制代码

你可能已经猜到了,咱们拿到纹理的widthheight,而后根据像素在纹理中的位置来计算redgreen的值,而后将新颜色写入回纹理中.你将看到相似这样的东西:

chapter10_4.png

接着,让咱们在屏幕中间画一个黑色的圆.用下面几行代码替换最后一行:

float2 uv = float2(gid) / float2(width, height);
uv = uv * 2.0 - 1.0;
bool inside = length(uv) < 0.5;
output.write(inside ? float4(0) : float4(red, green, 0, 1), gid); 
复制代码

你会看到相似这样的东西:

chapter10_5.png

咱们究竟是怎么作到的呢?其实,这是在着色中很经常使用的技术,叫作distance function.咱们使用length函数来肯定像素是否在屏幕中心也就是咱们圆的中心的0.5倍以内.注意,咱们归一化了uv向量来匹配窗口坐标范围 [-1,1].最后,咱们判断像素若是在内部就是黑色,不然就像原来同样,给它一个渐变色.

让咱们抽出这个圆内部/外部计算到一个距离函数中:

float dist(float2 point, float2 center, float radius) {
    return length(point - center) - radius;
} 
复制代码

而后,用下面几行替换咱们定义内部的那行:

float distToCircle = dist(uv, float2(0), 0.5);
bool inside = distToCircle < 0;
复制代码

看不到任何改变,但咱们如今有了一个能够轻易重用的函数.下一步,让咱们看看如何根据到圆的距离改变背景颜色,而不是仅根据像素的绝对位置.咱们经过计算像素到圆的距离来改变透明通道的值.用下面这行替换最后一行:

output.write(inside ? float4(0) : float4(1, 0.7, 0, 1) * (1 - distToCircle), gid);
复制代码

你应该看到相似这样的东西:

chapter10_6.png

很漂亮,对吧?如今咱们让它变成了日食,让咱们将它变得更真实一些.咱们须要另外一个圆(太阳),而且咱们想要让初始的圆向左一点,向下一点,这样它们就都能看到了.用下面几行替换咱们定义内部的那行:

float distToCircle2 = dist(uv, float2(-0.1, 0.1), 0.5);
bool inside = distToCircle2 < 0;
复制代码

你会看到相似下面的东西:

chapter10_7.png

咱们如今只是学会了着色技术的皮毛.在下一章节咱们将学习更复杂和动态的计算任务.特别感谢Chris Wood的建议.

源代码source code 已发布在Github上.

下次见!

相关文章
相关标签/搜索