如何将规范化设备坐标转换为Apple的Metal内核函数?

时间:2016-01-19 19:03:02

标签: ios 3d metal

我在Metal中有一个内核函数,我在纹理中传递,以便我可以对图像执行一些操作。我正在传递uint2 gid [[thread_position_in_grid]],它给出了像素坐标为整数。

要获得标准化的设备坐标,我可以在gid.xgid.y上进行一些简单的数学运算以及纹理宽度和高度。这是最好的方法吗?更好的方式?

3 个答案:

答案 0 :(得分:3)

你的方法很好。如果您不想查询内核函数内的纹理尺寸或创建缓冲区只是为了传递它们,您可以使用-[MTLComputeCommandEncoder setBytes:length:atIndex:]方法将纹理尺寸绑定在"临时&# 34;由Metal处理的各种缓冲区:

[computeEncoder setBytes:&dimensions length:sizeof(dimensions) atIndex:0]

答案 1 :(得分:1)

我认为你是对的,并且使用通常在GLSL中应用的相同方法是好方法:

  1. 计算纹理尺寸
  2. float2 texSize = float2(1/outTexture.get_with(),1/outTexture.get_height());

    1. 然后使用它来获得标准化的像素位置
    2. constexpr sampler s(address::clamp_to_edge, filter::linear, coord::normalized);

      //
      //  something to do...
      //
      float4 color = inTexture.sample(s,float2(gid)*texSize);
      
      //
      // something todo with pixel
      //
      
      outTexture.write(color,gid);
      

答案 2 :(得分:0)

问题中指定的方法效果很好。但是为了完成,使用非标准化(和/或标准化设备坐标)从纹理读取的另一种方法是使用采样器。

创建一个采样器:

id<MTLSamplerState> GetSamplerState()
{
    MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] autorelease];
    desc.minFilter = MTLSamplerMinMagFilterNearest;
    desc.magFilter = MTLSamplerMinMagFilterNearest;
    desc.mipFilter = MTLSamplerMipFilterNotMipmapped;
    desc.maxAnisotropy = 1;
    desc.sAddressMode = MTLSamplerAddressModeClampToEdge;
    desc.tAddressMode = MTLSamplerAddressModeClampToEdge;
    desc.rAddressMode = MTLSamplerAddressModeClampToEdge;

    // The key point: specifies that the sampler reads non-normalized coordinates
    desc.normalizedCoordinates = NO;

    desc.lodMinClamp = 0.0f;
    desc.lodMaxClamp = FLT_MAX;

    id <MTLSamplerState> sampler_state = nil;
    sampler_state = [[device_ newSamplerStateWithDescriptor:desc] autorelease];

    // Release the descriptor
    desc = nil;

    return sampler_state;
}

然后将其附加到您的计算命令编码器:

id <MTLComputeCommandEncoder> compute_encoder = [comand_buffer computeCommandEncoder];
id<MTLSamplerState> ss = GetSamplerState();

// Attach the sampler state to the encoder, say at sampler bind point 0
[compute_encoder setSamplerState:ss atIndex:0];

// And set your texture, say at texture bind point 0
[compute_encoder setTexture:my_texture atIndex:0];

最后在内核中使用它:

// An example kernel that samples from a texture,
// writes one component of the sample into an output buffer
kernel void compute_main( 
                texture2d<uint, access::sample> tex_to_sample [[ texture(0) ]],
                sampler smp [[ sampler(0) ]],
                device uint *out [[buffer(0)]],
                uint2 tid [[thread_position_in_grid]])
{
    out[tid] = tex_to_sample.sample(smp, tid).x;
}

使用采样器可以指定采样参数(如过滤)。您还可以使用连接到同一内核的不同采样器以不同方式访问纹理。采样器还避免了必须传递和检查纹理尺寸的边界。

请注意,也可以在计算内核中设置采样器。请参阅Metal Shading Language Specification

中的第2.6节采样器

最后,读取函数(使用gid,如问题中指定的)与使用采样器的采样之间的一个主要区别是read()采用整数坐标,而sample()采用浮点坐标。因此传递给样本的整数坐标将被转换为等效的浮点数。