ios - CoreImage:带有 .metallib 的 CIColorKernel.init 导致 EXEC_BAD_ACCESS
问题描述
我使用 Metal Shader Language 为 CoreImage 制作了 Perlin Noise 过滤器。我的第一个版本运行良好,并允许我指定 2 种颜色用作low
and high
。我的下一步是让它接受一个颜色映射,这样它就可以产生多色输出。颜色映射版本编译得很好,但是当我的 CIFilter 子类尝试使用该init(functionName:fromMetalLibraryData:)
方法从它创建一个 CIColorKernel 时,我得到一个EXEC_BAD_ACCESS
with code=1
。知道我的颜色映射实现可能会导致这种情况吗?
这是简单的 2 色版本:
#include <CoreImage/CoreImage.h>
using namespace metal;
constant int p[512] = {151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};
float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float grad(int hash, float x, float y, float z) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
float lerp(float t, float x, float y) { return x + t * (y - x); }
float noise(float x, float y, float z) {
int X = (int)floor(x) & 255;
int Y = (int)floor(y) & 255;
int Z = (int)floor(z) & 255;
x -= floor(x);
y -= floor(y);
z -= floor(z);
float u = fade(x);
float v = fade(y);
float w = fade(z);
int A = p[X ] + Y;
int AA = p[A ] + Z;
int AB = p[A + 1] + Z;
int B = p[X + 1] + Y;
int BA = p[B ] + Z;
int BB = p[B + 1] + Z;
float result = lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD
grad(p[BA ], x-1, y , z )), // BLENDED
lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS
grad(p[BB ], x-1, y-1, z ))),// FROM 8
lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS
grad(p[BA+1], x-1, y , z-1 )), // OF CUBE
lerp(u, grad(p[AB+1], x , y-1, z-1 ),
grad(p[BB+1], x-1, y-1, z-1 ))));
return (result + 1.0) / 2.0;
}
extern "C" float4 PerlinNoise (float4 lowColor, float4 highColor, float offsetX, float offsetY, float offsetZ, float scale, float contrast, coreimage::destination dest)
{
float val = noise(dest.coord().x * scale + offsetX, dest.coord().y * scale + offsetY, offsetZ);
return mix(lowColor, highColor, pow(val, contrast));
}
这是彩色地图版本:
#include <CoreImage/CoreImage.h>
using namespace metal;
constant uint8_t p[512] = {151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};
float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float grad(uint8_t hash, float x, float y, float z) {
uint8_t h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
float lerp(float t, float x, float y) { return x + t * (y - x); }
float noise(float x, float y, float z) {
uint8_t X = (uint8_t)floor(x) & 255;
uint8_t Y = (uint8_t)floor(y) & 255;
uint8_t Z = (uint8_t)floor(z) & 255;
x -= floor(x);
y -= floor(y);
z -= floor(z);
float u = fade(x);
float v = fade(y);
float w = fade(z);
uint8_t A = p[X ] + Y;
uint8_t AA = p[A ] + Z;
uint8_t AB = p[A + 1] + Z;
uint8_t B = p[X + 1] + Y;
uint8_t BA = p[B ] + Z;
uint8_t BB = p[B + 1] + Z;
float result = lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD
grad(p[BA ], x-1, y , z )), // BLENDED
lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS
grad(p[BB ], x-1, y-1, z ))),// FROM 8
lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS
grad(p[BA+1], x-1, y , z-1 )), // OF CUBE
lerp(u, grad(p[AB+1], x , y-1, z-1 ),
grad(p[BB+1], x-1, y-1, z-1 ))));
return (result + 1.0) / 2.0;
}
float4 colormapLookup(float value, size_t count, float indices[], float4 colormap[]) {
// If the value is at the outside boundary, just return the boundary color.
if (value < indices[0]) {
return colormap[0];
} else if (value >= indices[count - 1]) {
return colormap[count - 1];
}
// Find which 2 indices the value falls between.
size_t index = 0;
while (value < indices[index] && index < count - 1) {
index++;
}
float startIndex = indices[index];
float endIndex = indices[index+1];
// Calculate the normalized offset between those indies.
float offset = (value - startIndex) / (endIndex - startIndex);
// Return the blended color for that offest.
return mix(colormap[index], colormap[index+1], offset);
}
extern "C" float4 PerlinNoise (size_t count, float indices[], float4 colormap[], float offsetX, float offsetY, float offsetZ, float scale, float contrast, coreimage::destination dest)
{
float val = noise(dest.coord().x * scale + offsetX, dest.coord().y * scale + offsetY, offsetZ);
return colormapLookup(pow(val, contrast), count, indices, colormap);
}
这是我尝试实例化 CIColorKernel 的方法。EXEC_BAD_ACCESS
发生在调用 CIColorKernel init 函数的那一行:
static var kernel: CIColorKernel? = {
guard let url = Bundle.main.url(forResource: "PerlinNoiseGenerator", withExtension: "ci.metallib") else { return nil }
do {
let data = try Data(contentsOf: url)
return try CIColorKernel(functionName: "PerlinNoise", fromMetalLibraryData: data)
} catch {
print("[ERROR] Failed to create CIColorKernel: \(error)")
}
return nil
}()
编辑:添加了实例化 CIColorKernel 的 Swift 代码。
解决方案
根据 Frank 的评论,我将我的 Perlin 噪声过滤器恢复到其原始功能状态,而是使用此改进版本的过滤器应用颜色映射作为后续步骤,该CIColorMap
过滤器可以基于 [CGFloat: CIColor] 字典生成自己的渐变图像.
import CoreImage
class ImprovedColorMap: CIFilter {
enum ColorMapError: Error {
case unableToCreateCGContext
case unableToCreateCGGradient
}
private var _colorMap: [CGFloat: CIColor] = [
0.0: .black,
1.0: .white,
]
private var gradientImage: CIImage = CIImage()
private var mapFilter: CIFilter? = CIFilter(name: "CIColorMap")
var inputImage: CIImage?
var inputColorMap: [CGFloat: CIColor] {
get { _colorMap }
set {
guard newValue.keys.count >= 2 else {
print("ERROR: Color map must have at least 2 entries. Change will be ignored.")
return
}
_colorMap = newValue
generateGradient()
}
}
override var outputImage: CIImage? {
guard let filter = mapFilter else {
return nil
}
filter.setValue(gradientImage, forKey: kCIInputGradientImageKey)
filter.setValue(inputImage, forKey: kCIInputImageKey)
return filter.outputImage
}
init(colorMap: [CGFloat: CIColor]? = nil) {
if let colorMap = colorMap {
_colorMap = colorMap
}
super.init()
generateGradient()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func generateGradient() {
DispatchQueue.global(qos: .default).async {
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let context = CGContext(data: nil, width: 512, height: 16, bitsPerComponent: 8, bytesPerRow: 512 * 4, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
print("ERROR: Could not create CGContext.")
return
}
var locations: [CGFloat] = []
var components: [CGFloat] = []
for (location, color) in self._colorMap {
locations.append(location)
components.append(contentsOf: color.floatComponents)
}
guard let gradient = CGGradient(colorSpace: colorSpace, colorComponents: components, locations: locations, count: locations.count) else {
print("ERROR: Could not create CGGradient.")
return
}
context.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: 512.0, y: 0.0), options: [])
guard let image = context.makeImage() else {
print("ERROR: Failed to create image from context.")
return
}
DispatchQueue.main.async {
self.gradientImage = CIImage(cgImage: image)
}
}
}
}
效果很好,但现在我意识到传统的 Perlin Noise 对于逼真的地形来说太平滑了,所以接下来我将继续创建 Simplex Noise 金属内核。
推荐阅读
- aws-lambda - 通过 lambda 从 Cognito 用户池中获取用户属性
- c# - WPF:将 TabControl SelectedIndex 绑定到视图模型的枚举属性
- windows - 如何在 Cygwinx 上使用第三方 TTF 字体?Windows字体怎么样?
- react-native - react-native fetch 加入所有 set-cookie
- android - 如何在 Firebase 消息服务中发送除标题和正文之外的其他数据?(Node.js,安卓)
- angular5 - 从 Angular5 APP 到 .Net 4.7 后端 API 的不记名令牌 401 授权错误
- r - R中的flextable中的相同列名
- vba - VBA 在文件夹中生成最近修改的 .XLS 文件
- asp.net-identity - 重新启动身份服务器时再次请求声明
- r - dplyr 0.7.5 select() 行为的变化