2017-12-10 1 views
9

프로그래밍 방식으로 생성되는 이미지가 있으며이 이미지를 컴퓨팅 쉐이더에 텍스처로 보내려고합니다. 이 이미지를 생성하는 방법은 각각의 RGBA 구성 요소를 UInt8 값으로 계산하고이를 UInt32으로 결합하여 이미지의 버퍼에 저장하는 것입니다. 나는 다음과 같은 코드 조각이 작업을 수행 : RGBA32가 이동하고 UInt32 가치 창출을하는 작은 구조체이다UInt8 구성 요소 유형의 텍스처를 Metal 컴퓨팅 쉐이더에 전달

guard let cgContext = CGContext(data: nil, 
           width: width, 
           height: height, 
           bitsPerComponent: 8, 
           bytesPerRow: 0, 
           space: CGColorSpaceCreateDeviceRGB(), 
           bitmapInfo: RGBA32.bitmapInfo) else { 
            print("Unable to create CGContext") 
            return 
} 

guard let buffer = cgContext.data else { 
    print("Unable to create textures") 
    return 
} 
let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height) 
let heightFloat = Float(height) 
let widthFloat = Float(width) 
for i in 0 ..< height { 
    let latitude = Float(i + 1)/heightFloat 
    for j in 0 ..< width { 
    let longitude = Float(j + 1)/widthFloat 
    let x = UInt8(((sin(longitude * Float.pi * 2) * cos(latitude * Float.pi) + 1)/2) * 255) 
    let y = UInt8(((sin(longitude * Float.pi * 2) * sin(latitude * Float.pi) + 1)/2) * 255) 
    let z = UInt8(((cos(latitude * Float.pi) + 1)/2) * 255) 
    let offset = width * i + j 
    pixelBuffer[offset] = RGBA32(red: x, green: y, blue: z, alpha: 255) 
    } 
} 

let coordinateConversionImage = cgContext.makeImage() 

. 이 이미지는 UIImage으로 변환하여 내 사진 라이브러리에 저장할 수 있으므로 문제가 없습니다.

이 이미지를 컴퓨팅 쉐이더에 텍스처로 보내려고 할 때 문제가 발생합니다. 다음은 내 쉐이더 코드 :

kernel void updateEnvironmentMap(texture2d<uint, access::read> currentFrameTexture [[texture(0)]], 
           texture2d<uint, access::read> coordinateConversionTexture [[texture(1)]], 
           texture2d<uint, access::write> environmentMap [[texture(2)]] 
           uint2 gid [[thread_position_in_grid]]) 
{ 
    const uint4 pixel = {255, 127, 63, 255}; 
    environmentMap.write(pixel, gid); 
} 

이 코드의 문제는 내 텍스처의 종류가 32 비트 인 uint이고, 나는 32 비트 픽셀에게 난에서와 동일한 방식으로 생성 할 것입니다 CPU, 4 개의 8 비트 값 추가. 그러나, 내가 함께 추가 할 수있는 byte 유형이 없으므로 uint32을 만들었 기 때문에 Metal에서는 그렇게 할 수 없습니다. 그래서 제 질문은, 2D 텍스처를 처리하고 금속 계산 쉐이더에서 32 비트 픽셀을 설정하는 올바른 방법은 무엇입니까?

보너스 질문 : 입력 텍스처 유형으로 texture2d<float, access::read> 인 예제 쉐이더 코드를 보았습니다. 나는 그것이 0.0과 1.0 사이의 값을 나타내는 것으로 가정하고 있지만, 0과 255 사이의 값을 가진 unsigned int에는 어떤 장점이 있습니까?

편집 : 셰이더의 출력 텍스처 environmentMap은 입력 텍스처와 동일한 속성 (너비, 높이, pixelFormat 등)을 가지고 있습니다. 이것이 반 직관적이라고 생각하는 이유는 uint4을 픽셀로 설정한다는 것입니다. 즉, 4 개의 32 비트 값으로 구성되는 반면, 각 픽셀은 32 비트이어야합니다. 이 현재 코드에서 {255, 127, 63, 255}{2550, 127, 63, 255}과 정확히 동일한 결과를 가지므로 출력 텍스쳐에 쓰여지기 전에 어떻게 든 값이 0-255 사이에 고정됩니다. 그러나 이것은 극도로 반 직관적입니다.

답변

8

친숙한 것보다 좀 더 많은 마법이 있습니다. 그래서 나는 명료하게하려고 노력할 것입니다.

우선, 설계 상 Metal에서 텍스처의 저장 형식과 읽기/샘플을 얻는 형식 사이에 느슨한 연결이 있습니다. 으로 바인딩 된 텍스처를 통해 샘플링하면 float4의 구성 요소가 RGBA 순서로 제공되는 질감을 .bgra8Unorm 형식으로 가질 수 있습니다. 이러한 압축 된 바이트를 swizzled 구성 요소를 사용하는 float 벡터로 변환하면 Metal Shading Language Specification에 지정된대로 잘 문서화 된 변환 규칙이 적용됩니다.

저장 요소가 구성 요소 당 8 비트 등의 텍스처에 쓸 때 값은 기본 저장 형식에 맞게 고정됩니다. 텍스처가 norm 유형인지 여부에 따라 영향을받습니다. 형식에 norm이 포함되어 있으면 값이 0과 1 사이의 값을 지정한 것처럼 해석됩니다. 그렇지 않으면 읽은 값이 정규화되지 않습니다.

예 : 텍스처가 .bgra8Unorm하고 주어진 픽셀이 float 구성 요소를 요청하는 쉐이더에서 읽을 때 바이트 다음, [0, 64, 128, 255] 값이 포함 된 경우 당신이 그것을 샘플링 할 때, 당신은 [0.5, 0.25, 0, 1.0] 얻을 것이다. 반대로 형식이 .rgba8Uint 인 경우 [0, 64, 128, 255]이됩니다.텍스처의 저장 형식은 샘플링시 내용이 어떻게 해석되는지에 영향을 미칩니다.

질감의 픽셀 형식이 .rgba8Unorm 인 것으로 가정합니다. 그런 경우, 당신은 다음과 같이 커널을 작성하여 당신이 원하는 것을 얻을 수 있습니다 당신의 질감이 .rgba8Uint의 형식이있는 경우

kernel void updateEnvironmentMap(texture2d<float, access::read> currentFrameTexture [[texture(0)]], 
           texture2d<float, access::read> coordinateConversionTexture [[texture(1)]], 
           texture2d<float, access::write> environmentMap [[texture(2)]] 
           uint2 gid [[thread_position_in_grid]]) 
{ 
    const float4 pixel(255, 127, 63, 255); 
    environmentMap.write(pixel * (1/255.0), gid); 
} 

대조적으로는,이처럼를 작성하여 같은 효과를 얻을 수 있습니다 :

kernel void updateEnvironmentMap(texture2d<float, access::read> currentFrameTexture [[texture(0)]], 
           texture2d<float, access::read> coordinateConversionTexture [[texture(1)]], 
           texture2d<float, access::write> environmentMap [[texture(2)]] 
           uint2 gid [[thread_position_in_grid]]) 
{ 
    const float4 pixel(255, 127, 63, 255); 
    environmentMap.write(pixel, gid); 
} 

나는이 장난감의 예는 이해하지만 나는 전술 정보와 함께, 당신은 제대로 저장하고 샘플 값은 당신이 원하는 것을 달성하는 방법을 알아낼 수 있기를 바랍니다.

+0

워렌 덕분에 많은 일이 해결되었습니다. 또한, 이것에 대해 더 많은 정보를 원한다면 Metal Shading Language Specification의 Chapter 7.7 Texture Addressing and Conversion Rules를 참고하십시오. – halileohalilei

관련 문제