2017-12-10 112 views
9

Tôi có hình ảnh mà tôi tạo theo chương trình và tôi muốn gửi hình ảnh này dưới dạng kết cấu cho trình đổ bóng tính toán. Cách tôi tạo ra hình ảnh này là tôi tính từng thành phần RGBA là các giá trị UInt8 và kết hợp chúng thành một UInt32 và lưu nó vào bộ đệm của hình ảnh. Tôi làm điều này với đoạn mã sau:Chuyển họa tiết với kiểu thành phần UInt8 tới bóng đổ Tính toán kim loại

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() 

nơi RGBA32 là một cấu trúc nhỏ mà không được chuyển dịch và tạo ra giá trị UInt32. Hình ảnh này trở nên ổn khi tôi có thể chuyển đổi nó thành UIImage và lưu nó vào thư viện ảnh của tôi.

Sự cố xảy ra khi tôi cố gắng gửi hình ảnh này dưới dạng kết cấu cho trình đổ bóng tính toán. Dưới đây là mã shader của tôi:

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); 
} 

Vấn đề với mã này là các loại kết cấu của tôi là uint, đó là 32-bit, và tôi muốn tạo ra 32-bit pixel giống như cách tôi làm trên CPU, bằng cách thêm 4 giá trị 8 bit. Tuy nhiên, tôi không thể làm điều đó trên Kim loại vì không có loại byte mà tôi có thể nối thêm với nhau và tạo thành uint32. Vì vậy, câu hỏi của tôi là, cách chính xác để xử lý các họa tiết 2D và thiết lập các pixel 32-bit trên một bóng đổ tính toán bằng kim loại là gì?

Câu hỏi tiền thưởng: Ngoài ra, tôi đã thấy mã shader mẫu với texture2d<float, access::read> làm loại kết cấu đầu vào. Tôi giả định nó đại diện cho một giá trị giữa 0,0 và 1,0 nhưng những gì lợi thế mà không có trên một int unsigned với các giá trị giữa 0 và 255?

Chỉnh sửa: Để làm rõ, kết cấu đầu ra của bóng đổ, environmentMap, có cùng các thuộc tính giống nhau (chiều rộng, chiều cao, pixelFormat, v.v ...) làm kết cấu đầu vào. Tại sao tôi nghĩ rằng điều này phản trực giác là chúng tôi đang đặt một uint4 làm pixel, có nghĩa là nó bao gồm 4 giá trị 32 bit, trong khi mỗi pixel phải là 32 bit. Với mã hiện tại, {255, 127, 63, 255} có kết quả chính xác giống như {2550, 127, 63, 255}, có nghĩa là các giá trị bằng cách nào đó được kẹp giữa 0-255 trước khi được ghi vào kết cấu đầu ra. Nhưng điều này cực kỳ phản trực giác.

Trả lời

8

Có một chút phép thuật khi chơi có vẻ quen thuộc, vì vậy tôi sẽ cố gắng làm sáng tỏ.

Trước hết, theo thiết kế, có một kết nối lỏng lẻo giữa định dạng lưu trữ kết cấu trong Kim loại và loại bạn nhận được khi bạn đọc/lấy mẫu. Bạn có thể có kết cấu ở định dạng .bgra8Unorm, khi được lấy mẫu qua một kết cấu bị ràng buộc là texture2d<float, access::sample> sẽ cung cấp cho bạn một số float4 với các thành phần của nó theo thứ tự RGBA. Việc chuyển đổi từ các byte được đóng gói đó thành véc-tơ nổi với các thành phần bị chuyển đổi theo các quy tắc chuyển đổi được ghi chép đầy đủ như được quy định trong Đặc tả Ngôn ngữ Shading Metal. Nó cũng là trường hợp, khi ghi vào một kết cấu có lưu trữ (ví dụ) 8 bit cho mỗi thành phần, các giá trị sẽ được kẹp để phù hợp với định dạng lưu trữ cơ bản. Điều này tiếp tục bị ảnh hưởng bởi kết cấu có phải là loại norm hay không: nếu định dạng có chứa norm, các giá trị được hiểu như thể chúng đã chỉ định giá trị từ 0 đến 1. Nếu không, giá trị bạn đọc không được chuẩn hóa.

Một ví dụ: nếu một kết cấu là .bgra8Unorm và một điểm ảnh được chứa các byte giá trị [0, 64, 128, 255], sau đó khi đọc trong một Shader rằng yêu cầu float thành phần, bạn sẽ nhận được [0.5, 0.25, 0, 1.0] khi bạn nếm thử nó. Ngược lại, nếu định dạng là .rgba8Uint, bạn sẽ nhận được [0, 64, 128, 255].Định dạng lưu trữ của kết cấu có tác dụng phổ biến về cách nội dung của nó được diễn giải khi lấy mẫu.

Tôi giả định rằng định dạng pixel của kết cấu của bạn giống như .rgba8Unorm. Nếu đó là trường hợp, bạn có thể đạt được những gì bạn muốn bằng cách viết kernel như thế này:

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); 
} 

Ngược lại, nếu kết cấu của bạn có một định dạng của .rgba8Uint, bạn sẽ nhận được tác dụng tương tự bằng cách viết nó như thế này:

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); 
} 

Tôi hiểu rằng đây là ví dụ về đồ chơi, nhưng tôi hy vọng rằng với thông tin trên, bạn có thể tìm ra cách lưu trữ và lấy mẫu giá trị đúng cách để đạt được những gì bạn muốn.

+0

Cảm ơn Warren, điều này đã làm sáng tỏ rất nhiều điều. Ngoài ra, nếu có ai muốn biết thêm thông tin về vấn đề này, Chương 7.7 của Quy định về Ngôn ngữ của Metal Shading 7.7 Địa chỉ Texture và Quy tắc Chuyển đổi là nơi để xem xét. – halileohalilei

Các vấn đề liên quan