背景上的 SCNNode 大图像导致崩溃
SCNNode big image on background cause crash
我正在尝试使用 ARKit
和 SceneKit
创建标尺应用程序。我决定根据测量的距离以编程方式创建标尺图像。
这是我用来绘制标尺的扩展:
extension UIImage {
static let dashLineWidth: CGFloat = 2.0
static let dashDistance: CGFloat = 163.0 / 25.4
static let rulerFont: UIFont = .systemFont(ofSize: 15.0, weight: .regular)
static let attributes: [NSAttributedStringKey: Any] = [
NSAttributedStringKey.font: rulerFont,
NSAttributedStringKey.foregroundColor: UIColor.black
]
static func drawRuler(width: CGFloat) -> UIImage? {
let cm = width * 100 // width in centimeters
let size = CGSize(width: dashDistance * cm * 10, height: 50.0)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
let background = UIBezierPath(rect: CGRect(origin: .zero, size: size))
context.addPath(background.cgPath)
context.setFillColor(UIColor.white.cgColor)
context.fillPath()
var i: CGFloat = 0.0
var counter: Int = 0
while i < size.width {
let isLongDash = counter % 10 == 0
let isPartDash = counter % 5 == 0
let dashHeight: CGFloat = size.height * (isLongDash ? 0.25 : isPartDash ? 0.15 : 0.07)
UIColor.black.setFill()
UIRectFill(CGRect(x: i - dashLineWidth / 2, y: 0.0, width: dashLineWidth, height: dashHeight))
if isLongDash && counter != 0 {
let value = "\(counter / 10)"
let valueSize: CGSize = value.size(withAttributes: attributes)
value.draw(at: CGPoint(x: i - dashLineWidth / 2 - valueSize.width / 2, y: dashHeight + 5.0), withAttributes: attributes)
}
i += dashDistance
counter += 1
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func crop(to width: CGFloat, initialWidth: CGFloat) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: (width / initialWidth) * size.width * scale, height: size.height * scale)
guard let croppedCGImage: CGImage = cgImage?.cropping(to: rect) else { return nil }
return UIImage(cgImage: croppedCGImage)
}
}
所以一开始我只绘制一次 0.5 米的图像以获得更好的性能,然后每次只裁剪需要的部分以在 SCNNode
中显示。
这是我在 SCNNode
class 中尝试的内容:
var ruler: SCNNode = initRuler()
var initialWidth: CGFloat = 0.5
var rulerImage: UIImage? = UIImage.drawRuler(width: initialWidth)
func updateRuler() {
guard let geometry = ruler.geometry as? SCNBox else {
fatalError("Geometry is not SCNBox")
}
let width = geometry.width // in meters
if width > initialWidth - 0.05 {
initialWidth += 0.5
rulerImage = UIImage.drawRuler(width: initialWidth)
}
guard let croppedImage = rulerImage?.crop(to: width, initialWidth: initialWidth) else { return }
let texture = SKTexture(image: croppedImage)
let material = SCNMaterial()
material.diffuse.contents = texture
geometry.materials = [material]
}
当 SCNNode
的大小变大并且图像也变大时,一切正常。所以大约1.3米我撞车了
validateTextureDimensions:759: failed assertion `MTLTextureDescriptor
has width (16501) greater than the maximum allowed size of 16384.'
如有任何帮助,我们将不胜感激。我在想是否可以将图像分成几部分,然后分配给 material。或者还有其他方法吗?
首先,最好的办法可能是从互联网上找到标尺的图像或使用 photoshop 之类的工具来制作您自己的标尺,因为以编程方式制作它并不是很快。该错误与称为 MTLTextureDescriptor 的东西有关,问题是您使用的数字太大,这可能是因为您 运行 使用的设备太小,无法容纳整张照片。
与其将单个 16501px
宽图像设置为一个 SCNode
代表整个标尺,不如将标尺构建成数百个 1cm
段,每个段使用您以编程方式为带有数字的标尺段绘制的纹理。
我正在尝试使用 ARKit
和 SceneKit
创建标尺应用程序。我决定根据测量的距离以编程方式创建标尺图像。
这是我用来绘制标尺的扩展:
extension UIImage {
static let dashLineWidth: CGFloat = 2.0
static let dashDistance: CGFloat = 163.0 / 25.4
static let rulerFont: UIFont = .systemFont(ofSize: 15.0, weight: .regular)
static let attributes: [NSAttributedStringKey: Any] = [
NSAttributedStringKey.font: rulerFont,
NSAttributedStringKey.foregroundColor: UIColor.black
]
static func drawRuler(width: CGFloat) -> UIImage? {
let cm = width * 100 // width in centimeters
let size = CGSize(width: dashDistance * cm * 10, height: 50.0)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
let background = UIBezierPath(rect: CGRect(origin: .zero, size: size))
context.addPath(background.cgPath)
context.setFillColor(UIColor.white.cgColor)
context.fillPath()
var i: CGFloat = 0.0
var counter: Int = 0
while i < size.width {
let isLongDash = counter % 10 == 0
let isPartDash = counter % 5 == 0
let dashHeight: CGFloat = size.height * (isLongDash ? 0.25 : isPartDash ? 0.15 : 0.07)
UIColor.black.setFill()
UIRectFill(CGRect(x: i - dashLineWidth / 2, y: 0.0, width: dashLineWidth, height: dashHeight))
if isLongDash && counter != 0 {
let value = "\(counter / 10)"
let valueSize: CGSize = value.size(withAttributes: attributes)
value.draw(at: CGPoint(x: i - dashLineWidth / 2 - valueSize.width / 2, y: dashHeight + 5.0), withAttributes: attributes)
}
i += dashDistance
counter += 1
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func crop(to width: CGFloat, initialWidth: CGFloat) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: (width / initialWidth) * size.width * scale, height: size.height * scale)
guard let croppedCGImage: CGImage = cgImage?.cropping(to: rect) else { return nil }
return UIImage(cgImage: croppedCGImage)
}
}
所以一开始我只绘制一次 0.5 米的图像以获得更好的性能,然后每次只裁剪需要的部分以在 SCNNode
中显示。
这是我在 SCNNode
class 中尝试的内容:
var ruler: SCNNode = initRuler()
var initialWidth: CGFloat = 0.5
var rulerImage: UIImage? = UIImage.drawRuler(width: initialWidth)
func updateRuler() {
guard let geometry = ruler.geometry as? SCNBox else {
fatalError("Geometry is not SCNBox")
}
let width = geometry.width // in meters
if width > initialWidth - 0.05 {
initialWidth += 0.5
rulerImage = UIImage.drawRuler(width: initialWidth)
}
guard let croppedImage = rulerImage?.crop(to: width, initialWidth: initialWidth) else { return }
let texture = SKTexture(image: croppedImage)
let material = SCNMaterial()
material.diffuse.contents = texture
geometry.materials = [material]
}
当 SCNNode
的大小变大并且图像也变大时,一切正常。所以大约1.3米我撞车了
validateTextureDimensions:759: failed assertion `MTLTextureDescriptor has width (16501) greater than the maximum allowed size of 16384.'
如有任何帮助,我们将不胜感激。我在想是否可以将图像分成几部分,然后分配给 material。或者还有其他方法吗?
首先,最好的办法可能是从互联网上找到标尺的图像或使用 photoshop 之类的工具来制作您自己的标尺,因为以编程方式制作它并不是很快。该错误与称为 MTLTextureDescriptor 的东西有关,问题是您使用的数字太大,这可能是因为您 运行 使用的设备太小,无法容纳整张照片。
与其将单个 16501px
宽图像设置为一个 SCNode
代表整个标尺,不如将标尺构建成数百个 1cm
段,每个段使用您以编程方式为带有数字的标尺段绘制的纹理。