前言
大家好,我是一牛,今天我和大家分享的是如何使用 Metal 渲染图片。在上一节我们学会了如何渲染三角形,在这个基础上,渲染一张图片就会变得很容易。
纹理
首先我们先来复习下纹理的定义。纹理(Texture
)是计算机图形学和图像处理中的一个重要概念,通常指用来为几何表面添加细节的二维或三维图像数据。在视觉上,纹理可以提供物体表面细节的逼真感,例如木纹、金属反光或粗糙度。在本文中我们通过图片生成纹理。
在Metal
中我们可以使用MetalKit
中的工具类MTKTextureLoader
来生成纹理。
swift 代码解读复制代码nonisolated func createTexture() async -> MTLTexture? {
let device = MTLCreateSystemDefaultDevice()!
let loader = MTKTextureLoader(device: device)
guard let url = Bundle.main.url(forResource: "lena_color", withExtension: "png") else {
return nil
}
let texture = try? await loader.newTexture(URL: url)
return texture
}
对于简单格式图片,我们可以使用这个工具类生成纹理,对于特殊格式使用这个方法生成纹理可能会失败。
纹理坐标系
也就是说屏幕的左上角对应纹理坐标(0, 0)
屏幕右下角对应纹理坐标(1.0,1.0)
, 屏幕中心点是纹理坐标(0.5,0.5)
。
大家都知道,Metal的绘制的基本图元是三角形,渲染一张图片我们只需要绘制两个三角形。
swift 代码解读复制代码let vertices = [
// 左上角
Vertex(pixelPosition: [-256, 256], textureCoordinate: [0.0, 0.0]),
// 左下角
Vertex(pixelPosition: [-256, -256], textureCoordinate: [0.0, 1.0]),
// 右下角
Vertex(pixelPosition: [ 256, -256], textureCoordinate: [1.0, 1.0]),
// 左上角
Vertex(pixelPosition: [-256, 256], textureCoordinate: [0.0, 0.0]),
// 右下角
Vertex(pixelPosition: [ 256, -256], textureCoordinate: [1.0, 1.0]),
// 右上角
Vertex(pixelPosition: [ 256, 256], textureCoordinate: [1.0, 0.0]),
]
(-256, 256)是图片的像素坐标,表示图片的左上角,它对应的纹理坐标是(0, 0)。图片像素坐标需要在顶点阶段转换成光栅化阶段需要的归一化坐标。为了演示方便,图片的长度和宽度恰好是256
设置纹理
less 代码解读复制代码commanderEncoder?.setFragmentTexture(texture, index: 0)
将生成的纹理交给片元函数处理。
顶点阶段
swift 代码解读复制代码vertex RasterizerData vertexShader(uint vertexID [[vertex_id]],
constant Vertex *vertices [[buffer(0)]],
constant uint2 *viewportSizePointer [[buffer(1)]]
) {
float2 pixelPosition = vertices[vertexID].pixelPosition;
float2 viewportSize = float2(*viewportSizePointer);
RasterizerData out;
out.position = float4(0.0, 0.0, 0.0, 1.0);
out.position.xy = pixelPosition / (viewportSize / 2.0);
out.textureCoordinate = vertices[vertexID].textureCoordinate;
return out;
}
和上一篇文章不同的是,我们传入了画布的尺寸。[[buffer(1)]]
表示我们插槽1
传入数据。
less 代码解读复制代码commanderEncoder?.setVertexBytes(&viewPortSize, length: MemoryLayout.stride, index: 1)
我们通过转化我们得到光栅化所需要的归一化坐标,可以看出图片会一直保持在画布的中间。
片元阶段
swift 代码解读复制代码fragment float4 fragmentShader(RasterizerData in [[stage_in]],
texture2d<half> colorTexture [[texture(0)]]
) {
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);
return float4(colorSample);
}
sampler 是 Metal Shading Language 中用于定义纹理采样方式的对象。
•mag_filter::linear: 当纹理被放大时,使用线性插值进行采样。
•min_filter::linear: 当纹理被缩小时,使用线性插值进行采样。
总结
通过本文的学习,我们了解了如何在 Metal 中加载纹理、设置纹理以及在着色器中采样纹理数据,最终实现了图片的渲染。相比于渲染简单的几何图形,纹理的引入让渲染内容更加丰富和多样化。下一步,我们将学习离屏渲染。
创作不易,请大家多多点赞收藏!感谢大家!
Demo
见github
评论记录:
回复评论: