簡體   English   中英

金屬芯圖 kernel 帶采樣器

[英]Metal core image kernel with sampler

我正在嘗試將 CIColorKernel 或 CIBlendKernel 與采樣器 arguments 一起使用,但程序崩潰了。 這是我編譯成功的着色器代碼。

extern "C" float4 wipeLinear(coreimage::sampler t1, coreimage::sampler t2, float time) {
    float2 coord1 = t1.coord();
    float2 coord2 = t2.coord();

    float4 innerRect = t2.extent();

    float minX = innerRect.x + time*innerRect.z;
    float minY = innerRect.y + time*innerRect.w;
    float cropWidth = (1 - time) * innerRect.w;
    float cropHeight = (1 - time) * innerRect.z;

    float4 s1 = t1.sample(coord1);
    float4 s2 = t2.sample(coord2);

   if ( coord1.x > minX && coord1.x < minX + cropWidth && coord1.y > minY && coord1.y <= minY + cropHeight) {
       return s1;
   } else {
      return s2;
   }
}

它在初始化時崩潰。

class CIWipeRenderer: CIFilter {
var backgroundImage:CIImage?
var foregroundImage:CIImage?
var  inputTime: Float = 0.0

static var kernel:CIColorKernel = { () -> CIColorKernel in

    let url = Bundle.main.url(forResource: "AppCIKernels", withExtension: "ci.metallib")!
    let data = try! Data(contentsOf: url)
    return try! CIColorKernel(functionName: "wipeLinear", fromMetalLibraryData: data) //Crashes here!!!!
    
}()

override var outputImage: CIImage? {
    guard let backgroundImage = backgroundImage else {
        return nil
    }
    
    guard let foregroundImage = foregroundImage else {
        return nil
    }
    
    return CIWipeRenderer.kernel.apply(extent: backgroundImage.extent, arguments: [backgroundImage, foregroundImage, inputTime])
}

}

它在 try 行中崩潰並出現以下錯誤:

 Fatal error: 'try!' expression unexpectedly raised an error: Foundation._GenericObjCError.nilError

如果我用以下代碼替換 kernel 代碼,它就像一個魅力:

  extern "C" float4 wipeLinear(coreimage::sample_t s1, coreimage::sample_t s2, float time)
{
     return mix(s1, s2, time);
}

所以代碼中沒有明顯的錯誤,比如傳遞了錯誤的function名字左右。

是的,您不能在CIColorKernelCIBlendKernel中使用采樣器。 這些內核針對從輸入像素到 output 像素的 1:1 映射的用例進行了優化。 這允許 Core Image 在一個命令緩沖區中執行多個這些內核,因為它們不需要任何中間緩沖區寫入。
sampler將允許您在任意坐標處對輸入進行采樣,這在這種情況下是不允許的。

您可以簡單地使用CIKernel代替。 當您需要更自由地對輸入進行采樣時,可以使用它。

對於您的用例,您實際上可以使用CIColorKernel 您只需要將渲染目標的范圍也傳遞給 kernel,然后您就不需要采樣器來訪問它。

kernel 看起來像這樣:

extern "C" float4 wipeLinear(coreimage::sample_t t1, coreimage::sample_t t2, float4 destinationExtent, float time, coreimage::destination destination) {
    float minX = destinationExtent.x + time * destinationExtent.z;
    float minY = destinationExtent.y + time * destinationExtent.w;
    float cropWidth = (1.0 - time) * destinationExtent.w;
    float cropHeight = (1.0 - time) * destinationExtent.z;

    float2 destCoord = destination.coord();

   if ( destCoord.x > minX && destCoord.x < minX + cropWidth && destCoord.y > minY && destCoord.y <= minY + cropHeight) {
       return t1;
   } else {
      return t2;
   }
}

你這樣稱呼它:

let destinationExtent = CIVector(cgRect: backgroundImage.extent)
return CIWipeRenderer.kernel.apply(extent: backgroundImage.extent, arguments: [backgroundImage, foregroundImage, destinationExtent, inputTime])

請注意,kernel 中的最后一個destination參數由 Core Image 自動傳遞。 您無需使用arguments傳遞它。

獎勵答案:

對於這種混合效果,您實際上根本不需要任何 kernel。 您可以通過簡單的裁剪和合成來實現所有這些:

class CIWipeRenderer: CIFilter {
    var backgroundImage:CIImage?
    var foregroundImage:CIImage?
    var inputTime: CGFloat = 0.0

    override var outputImage: CIImage? {
        guard let backgroundImage = backgroundImage else { return nil }
        guard let foregroundImage = foregroundImage else { return nil }

        // crop the foreground based on time
        var foregroundCrop = foregroundImage.extent
        foregroundCrop.size.width *= inputTime
        foregroundCrop.size.height *= inputTime

        return foregroundImage.cropped(to: foregroundCrop).composited(over: backgroundImage)
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM