你好,三角形

498 阅读2分钟

前言

大家好,我是一牛。今天我想和大家分享的是使用Metal 绘制三角形。绘制三角形是图形学的基础,打好基础是我们通向图形学世界关键所在。本文将介绍如何构建一个渲染管线来绘制三角形。

什么是渲染管线

渲染管线(Render Pipeline) 指的是图形渲染的一个整体过程,负责定义从顶点数据到像素输出的完整流程,包括顶点着色,光栅化,片元着色等。它规定了数据如何从输入的顶点、纹理和其他资源流经一系列固定和可编程阶段,最终生成屏幕上的像素。在Metal中 顶点着色和片元着色是可编程阶段。

Screenshot 2024-11-18 at 15.43.18.png

顶点阶段

struct RasterizerData {
    float4 position [[position]];
    float4 color;
};
struct Vertex {
    float4 position;
    float4 color;
};
vertex RasterizerData vertexShader(uint vertexID [[vertex_id]],
                           constant Vertex *vertices [[buffer(0)]]) {
    RasterizerData out;
    out.position = vertices[vertexID].position;
    out.color = vertices[vertexID].color;
    return out;
}

[[vertext_id]] 是Metal的关键字,通过它我们获取到对应的输入的顶点坐标。[[buffer(0)]]表示我们将在插槽0处传入数据,例子中是顶点数组。

Metal中,顶点着色器需要提供在裁剪空间中的齐次坐标(x, y, z, w),我们需要使用关键字[[position]]来表示,在随后的光栅化阶段通过 x/w, y/w, z/w 得到三维归一化坐标。因为我们绘制的是2D图形, 为了方便,我们可以将输入的顶点的齐次坐标w设为 1z分量设置为0.

let vertices = [
    //左下角
    Vertex(position: [-1.0, -1.0, 0.0, 1.0], color: [1.0, 0.0, 0.0, 1.0]),
    //正上方
    Vertex(position: [ 0.0,  1.0, 0.0, 1.0], color: [1.0, 0.5, 0.0, 1.0]),
    //右下角
    Vertex(position: [ 1.0, -1.0, 0.0, 1.0], color: [0.0, 1.0, 0.0, 1.0]),
]

对应屏幕的坐标系如下

Screenshot 2024-11-18 at 14.49.07.png

片元阶段

顶点着色器的输出是光栅化阶段的输入, 光栅化阶段会将顶点坐标映射到屏幕坐标并生成片元。关键字

[[stage_in]]表明片元函数的输入是光栅化的输出,这里我们直接返回输入的颜色值。

fragment float4 fragmentShader(RasterizerData in [[stage_in]]) {
    return in.color;
}

创建渲染管线

let library = device.makeDefaultLibrary()
let vertextFunc = library?.makeFunction(name: "vertexShader")
let fragmentFunc = library?.makeFunction(name: "fragmentShader")
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertextFunc
pipelineDescriptor.fragmentFunction = fragmentFunc
pipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
do {
    pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
} catch {
    fatalError("fail to create pipelineState")
}

创建好顶点函数和片元函数后,我们需要使用将它们绑定到MTLRenderPipelineDescriptor 并创建渲染管线

编码绘制命令

commanderEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertices.count)

总结

通过本文,我们学习了如何使用 Metal 绘制一个简单的三角形。从构建渲染管线到编写顶点和片元着色器,再到将绘制命令编码到命令缓冲区,每一步都为实现图形渲染奠定了基础。其中掌握图形渲染管线是重中之重。

图形学的世界广阔无边,从 Metal 入门绘制三角形开始,一步步深入探索,最终实现更加精美复杂的图形渲染!

源码