我有一个1080x1920像素的纹理。我试图在MTKView
上渲染它,这不是相同的长宽比。(即iPad/iPhone X全屏)。
这是我如何渲染MTKView
的纹理:
private func render(_ texture: MTLTexture, withCommandBuffer commandBuffer: MTLCommandBuffer, device: MTLDevice) {
guard let currentRenderPassDescriptor = metalView?.currentRenderPassDescriptor,
let currentDrawable = metalView?.currentDrawable,
let renderPipelineState = renderPipelineState,
let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: currentRenderPassDescriptor) else {
semaphore.signal()
return
}
encoder.pushDebugGroup("RenderFrame")
encoder.setRenderPipelineState(renderPipelineState)
encoder.setFragmentTexture(texture, index: 0)
encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
encoder.popDebugGroup()
encoder.endEncoding()
// Called after the command buffer is scheduled
commandBuffer.addScheduledHandler { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.didRender(texture: texture)
strongSelf.semaphore.signal()
}
commandBuffer.present(currentDrawable)
commandBuffer.commit()
}
我希望纹理像.scaleAspectFill
一样在UIView
上渲染,我正在学习Metal
,所以我不确定应该在哪里寻找它(.metal
文件、管道、视图本身、编码器等)。
谢谢!
编辑:下面是着色器代码:
#include <metal_stdlib> using namespace metal;
typedef struct {
float4 renderedCoordinate [[position]];
float2 textureCoordinate; } TextureMappingVertex;
vertex TextureMappingVertex mapTexture(unsigned int vertex_id [[ vertex_id ]]) {
float4x4 renderedCoordinates = float4x4(float4( -1.0, -1.0, 0.0, 1.0 ),
float4( 1.0, -1.0, 0.0, 1.0 ),
float4( -1.0, 1.0, 0.0, 1.0 ),
float4( 1.0, 1.0, 0.0, 1.0 ));
float4x2 textureCoordinates = float4x2(float2( 0.0, 1.0 ),
float2( 1.0, 1.0 ),
float2( 0.0, 0.0 ),
float2( 1.0, 0.0 ));
TextureMappingVertex outVertex;
outVertex.renderedCoordinate = renderedCoordinates[vertex_id];
outVertex.textureCoordinate = textureCoordinates[vertex_id];
return outVertex; }
fragment half4 displayTexture(TextureMappingVertex mappingVertex [[ stage_in ]],texture2d<float, access::sample> texture [[ texture(0) ]]) {
constexpr sampler s(address::clamp_to_edge, filter::linear);
return half4(texture.sample(s, mappingVertex.textureCoordinate));
}
4条答案
按热度按时间0h4hbjxa1#
处理“金属”纹理或“一般金属”时,需要从以下几个常规事项开始:
renderPassDescriptor
中设置它们。但你现在不需要关心这个。你应该关心的唯一一件事是什么,要在解析纹理中渲染解析纹理中的1080 x1920像素纹理的位置和部分(可能具有不同的纵横比)。我们希望完全填充(“scaleAspectFill”)解析纹理,所以我们在片段着色器中保留renderedCoordinates
不变。它们在整个解析纹理上定义了一个矩形,这意味着在解析纹理中的每一个像素都会调用片段着色器。2接下来,我们将简单地改变纹理坐标。ratio = width / height
,解析纹理为r_tex
,你想要渲染的纹理为tex
。1.因此,假设解析纹理没有相同的纵横比,则有两种可能的情况:
1.您要渲染的纹理的纵横比比比您的解析纹理(金属渲染的纹理)的纵横比大,这意味着您要渲染的纹理比解析纹理的宽度大。在这种情况下,我们保留坐标的
y
值不变。纹理坐标的x
值将被更改:这些值必须规格化,因为纹理采样需要0到1范围内的坐标:
我们有了新的纹理坐标:
这将产生这样的效果:纹理的顶部或底部不会被剪切,但左侧和右侧的一些外部部分将被剪切,即不可见。
1.你想要渲染的纹理的长宽比比比你的解析纹理的长宽比小**。这个过程和第一个场景一样,但是这次我们要改变
y
坐标这应该渲染你的纹理,使解析纹理被完全填充,并且你的纹理的纵横比在x轴上保持不变。保持y轴的工作原理类似。另外,你必须检查纹理的哪一边更大/更小,并将其纳入你的计算中。这将像使用
scaleAspectFill
时一样裁剪你的纹理的一部分。请注意,上面的解决方案是未经测试的。但我希望它是有帮助的。一定要访问金属最佳实践文档不时,这是非常有帮助的,以获得基本概念的权利。bmp9r5qi2#
所以顶点着色器直接决定了源纹理被拉伸到视口的尺寸。你渲染的是一个充满视口的四边形,因为它的坐标在水平和垂直方向上都在规格化设备坐标系的端点([-1,1])。
你在同一个范围内对角Map源纹理,这是因为你为纹理坐标指定了纹理坐标空间的极值([0,1])。
有很多方法可以实现你想要的。你可以通过缓冲区将顶点坐标传递给着色器,而不是硬编码。这样,你就可以在应用代码中计算适当的值。你可以在渲染目标中计算所需的目标坐标,用NDC表示。所以,从概念上讲,类似于
left_ndc = (left_pixel / target_width) * 2 - 1
等。或者(可能更简单),您可以保持着色器不变,并更改绘制操作的视口,以仅针对渲染目标的适当部分。
k4aesqcs3#
1awuesterose的答案是有效的,我唯一要补充的是使用
abs(tex.width - r_tex.width)
。这是我的代码的样子如果有用的话。
ppcbkaq54#
另一方面,我决定在渲染器中使用CPU进行纹理坐标计算,并将矩阵注入着色器函数中,而不是对每个像素进行相同的计算。
着色器
渲染器