当我在3d对象模型上应用特定纹理时,我现在有点卡住了。
最简单的解决方案是做let test = SCNScene(named: "models.scnassets/modelFolder/ModelName.obj")
,但这需要mtl文件映射纹理文件直接在它里面,这是不可能与我目前的工作流程。
根据我目前的理解,这让我可以选择使用散射函数将纹理应用于特定的语义,就像这样:
if let url = URL(string: obj) {
let asset = MDLAsset(url: url)
guard let object = asset.object(at: 0) as? MDLMesh else {
print("Failed to get mesh from asset.")
self.presentAlert(title: "Warning", message: "Could not fetch the model.", firstBtn: "Ok")
return
}
// Create a material from the various textures with a scatteringFunction
let scatteringFunction = MDLScatteringFunction()
let material = MDLMaterial(name: "material", scatteringFunction: scatteringFunction)
let property = MDLMaterialProperty(name: "texture", semantic: .baseColor, url: URL(string: self.textureURL))
material.setProperty(property)
// Apply the texture to every submesh of the asset
object.submeshes?.forEach {
if let submesh = $0 as? MDLSubmesh {
submesh.material = material
}
}
// Wrap the ModelIO object in a SceneKit object
let node = SCNNode(mdlObject: object)
let scene = SCNScene()
scene.rootNode.addChildNode(node)
// Set up the SceneView
sceneView.scene = scene
...
}
实际的问题是语义。3d模型是在虚幻中制作的,对于许多模型,有一个png纹理,其中有3个语义,即环境遮挡,粗糙度和金属。环境遮挡需要应用在红色通道上,粗糙度应用在红色通道上,金属应用在蓝色通道上。
我怎样才能做到这一点?MdlMaterialSemantic具有所有这些可能的语义,但金属,环境遮挡和粗糙度都是分开的。我试着简单地应用纹理,但显然这不是很好。
考虑到我的。png纹理有所有这3个"包装"在不同的频道下,我怎么处理这个?我在想,也许我可以以某种方式使用一个小脚本添加映射到纹理的mtl文件在我的端在应用程序中直接,但这似乎粗略lol..
如果没有办法,我还有什么其他选择?我也一直在尝试使用fbx文件与assimpKit,但我无法加载任何纹理,只是黑色的模型…
我愿意接受任何建议,如果需要更多的信息,请让我知道!非常感谢!
对不起,我没有足够的代表来评论,但这可能更像是一个评论而不是一个答案!
你是否尝试过分别加载纹理png图像(作为NS/UI/CGImage),然后手动将其分成三个通道,然后分别应用这些通道?(分成三个独立的频道并不像想象的那么简单……但你可以使用这种灰度转换作为指导,每次只做一个通道。
一旦你在SceneKit中有了你的对象,修改这些材料可能会稍微容易一些。一旦你有了SCNNode
、SCNGeometry
和SCNMaterial
,你就可以访问任何这些材料,并将.contents
属性设置为几乎任何东西(包括XXImage)。
编辑:
这是一个扩展,您可以尝试使用加速从CGImage提取单个通道。你可以从NSImage/UIImage中获得CGImage,这取决于你是在Mac还是iOS上(你可以直接将文件加载到其中一种图像格式中)。
我刚刚从上面的链接改编了代码,我对加速框架不是很有经验,所以使用你自己的风险!但希望这能让你走上正确的道路。
extension CGImage {
enum Channel {
case red, green, blue
}
func getChannel(channel: Channel) -> CGImage? {
// code adapted from https://developer.apple.com/documentation/accelerate/converting_color_images_to_grayscale
guard let format = vImage_CGImageFormat(cgImage: cgImage) else {return nil}
guard var sourceImageBuffer = try? vImage_Buffer(cgImage: cgImage, format: format) else {return nil}
guard var destinationBuffer = try? vImage_Buffer(width: Int(sourceImageBuffer.width), height: Int(sourceImageBuffer.height), bitsPerPixel: 8) else {return nil}
defer {
sourceImageBuffer.free()
destinationBuffer.free()
}
let redCoefficient: Float = channel == .red ? 1 : 0
let greenCoefficient: Float = channel == .green ? 1 : 0
let blueCoefficient: Float = channel == .blue ? 1 : 0
let divisor: Int32 = 0x1000
let fDivisor = Float(divisor)
var coefficientsMatrix = [
Int16(redCoefficient * fDivisor),
Int16(greenCoefficient * fDivisor),
Int16(blueCoefficient * fDivisor)
]
let preBias: [Int16] = [0, 0, 0, 0]
let postBias: Int32 = 0
vImageMatrixMultiply_ARGB8888ToPlanar8(&sourceImageBuffer,
&destinationBuffer,
&coefficientsMatrix,
divisor,
preBias,
postBias,
vImage_Flags(kvImageNoFlags))
guard let monoFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 8,
colorSpace: CGColorSpaceCreateDeviceGray(),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
renderingIntent: .defaultIntent) else {return nil}
guard let result = try? destinationBuffer.createCGImage(format: monoFormat) else {return nil}
return result
}
}