gpt4 book ai didi

ios - 在MTKView上渲染MTLTexture不能保持宽高比

转载 作者:搜寻专家 更新时间:2023-11-01 06:30:31 32 4
gpt4 key购买 nike

我有一个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));
}

最佳答案

在处理“金属”纹理或“金属”时,通常要从以下几件事开始:

  • 您应考虑点和像素之间的差异,请参阅文档here。 UIView子类的frame属性(因为MTKView是其中之一)始终为您提供视图的宽度和高度(以磅为单位)。
  • 通过contentScaleFactor选项控制从点到实际像素的映射。 MTKView会自动选择合适的长宽比与设备的实际像素匹配的纹理。例如,iPhone X上MTKView的基础纹理的分辨率为2436 x 1125(以像素为单位的实际显示大小)。这已记录为here:“MTKView类自动支持本机屏幕缩放。默认情况下,始终保证视图当前可绘制对象的大小与视图本身的大小匹配。”
  • 如此处所述,.scaleAspectFill选项“缩放内容以填充视图的大小。内容的某些部分可能会被裁剪以填充视图的边界”。您要模拟此行为。
  • 使用Metal进行渲染无非就是“绘制”解析纹理,该纹理由MTKView自动设置。但是,您仍然拥有完全的控制权,可以通过手动创建纹理并将其设置在renderPassDescriptor中来自行完成。但是您现在不需要关心这一点。您应该关心的唯一事情是要在您的解析纹理(可能具有不同的宽高比)中呈现的解析纹理中1080x1920像素纹理的内容,位置和部分。我们要完全填充(“scaleAspectFill”)解析纹理,因此我们将renderedCoordinates保留在片段着色器中。在整个解析纹理上定义一个矩形,这意味着将为解析纹理中的每个像素调用片段着色器。接下来,我们将简单地更改纹理坐标。
  • 让我们将宽高比定义为ratio = width / height,将解析纹理定义为r_tex,将要渲染的纹理定义为tex
  • 因此,假设您的可分辨纹理的纵横比不同,则有两种可能的情况:
  • 要渲染的纹理的纵横比比解析的纹理(Metal渲染的纹理)的纵横比更大,这意味着要渲染的纹理的宽度大于解析的纹理的宽度。在这种情况下,我们保持坐标的y值不变。纹理坐标的x值将被更改:
    x_left  = 0 + ((tex.width - r_tex.width) / 2.0)
    x_right = tex_width - ((tex.width - r_tex_width) / 2.0)

    这些值必须归一化,因为纹理样本需要的坐标范围是0到1:
    x_left  = x_left / tex.width
    x_right = x_right / tex.width

    我们有新的纹理坐标:
    topLeft = float2(x_left,0)
    topRight = float2(x_right,0)
    bottomLeft = float2(x_left,1)
    bottomRight = float2(x_right,1)

    这将具有以下效果:纹理的顶部或底部不会被切除,但是左侧和右侧的一些外部部分将被剪切,即不可见。
  • 要渲染的纹理的纵横比比解析的纹理的纵横比小。步骤与第一种情况相同,但是这次我们将更改y坐标

  • 这应该渲染纹理,以便完全填充可分辨的纹理,并且在x轴上保持纹理的纵横比。保持y轴的工作原理类似。此外,您还必须检查纹理的哪一侧更大或更小,并将其合并到计算中。这将像使用 scaleAspectFill一样剪切纹理的一部分。请注意,上述解决方案未经测试。但我希望它会有所帮助。请确保不时访问 Metal Best Practices文档,这对正确理解基本概念很有帮助。玩金属吧!

    关于ios - 在MTKView上渲染MTLTexture不能保持宽高比,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48064936/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com