RealityKit - 如何设置 ModelEntity 的透明度?

6
在 SceneKit 中,有很多选项可供使用,例如:
  • 通过 SCNMaterial.(diffuse|emission|ambient|...).contents 使用 UIColor 的 alpha 通道
  • 使用 SCNMaterial.transparency(一个从 0.0 到 1.0 的 CGFloat 值)
  • 使用 SCNMaterial.transparent(另一个 SCNMaterialProperty 属性)
  • 使用 SCNNode.opacity(一个从 0.0(完全透明)到 1.0(完全不透明)的 CGFloat 值)
我想知道在 RealityKit 中是否有一种方式可以为 ModelEntity 设置透明度/不透明度/alpha。
3个回答

10

RealityKit 1.0(iOS | macOS)

在RealityKit 1.0中有一种解决方案,可以让您控制物体的透明度。您可以使用SimpleMaterial()baseColortintColor实例属性来实现:

 var tintColor: NSColor { get set }
 var baseColor: NSColor { get set }

 var tintColor: UIColor { get set }
 var baseColor: UIColor { get set }

即使在iOS中,它也可以完美运行,甚至带有颜色参数。
import UIKit
import RealityKit

class GameViewController: UIViewController {
    
    @IBOutlet var arView: ARView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        arView.backgroundColor = .black

        var material = SimpleMaterial()

        material.tintColor = UIColor.init(red: 1.0, 
                                        green: 1.0, 
                                         blue: 1.0, 
                                        alpha: 0.025)

        material.baseColor = MaterialColorParameter.color(UIColor.red)

        let mesh: MeshResource = .generateSphere(radius: 0.7)
        let modelEntity = ModelEntity(mesh: mesh,
                                 materials: [material])
        let anchor = AnchorEntity()
        anchor.addChild(modelEntity)
        arView.scene.anchors.append(anchor)
    }
}

enter image description here

macOS解决方案(RealityKit 1.0的纹理示例):
var material = SimpleMaterial()
         
// CYAN TINT and SEMI-TRANSPARENT ALPHA   
material.tintColor = NSColor.init(red: 0.0, green: 1.0, blue: 1.0, alpha: 0.5)

material.baseColor = try! MaterialColorParameter.texture(TextureResource.load(contentsOf: url))
material.roughness = MaterialScalarParameter(floatLiteral: 0.0)
material.metallic = MaterialScalarParameter(floatLiteral: 1.0)
    
// CUBE WAS MADE IN REALITY COMPOSER
cubeComponent.materials = [material]
    
// SPHERE IS MADE PROGRAMMATICALLY
let mesh: MeshResource = .generateSphere(radius: 0.7)

let sphereComponent = ModelComponent(mesh: mesh,
                                materials: [material])

anchor.steelBox!.components.set(cubeComponent)
anchor.components.set(sphereComponent)
arView.scene.anchors.append(anchor)

如果您不需要模型上的任何纹理(只需颜色和透明度),您可以通过baseColor实例属性来控制透明度。
material.baseColor = MaterialColorParameter.color(.init(red: 0.0,
                                                      green: 1.0, 
                                                       blue: 1.0, 
                                                      alpha: 0.5))

如果您的场景中包含两种类型的对象 - 一种是在Reality Composer中创建的,另一种是在Xcode中以编程方式创建的,并且您将相同的材质分配给这两个对象 - 编译后的应用程序会出现一些渲染问题(请查看下面的图片)。

enter image description here

这是由于RealityKit的工作不稳定(因为该框架目前还太年轻)。我认为在下一个版本的RealityKit中,诸如“Reality Composer模型上缺少纹理”和“球体上残留奇怪的反射”等错误将被消除。


RealityKit 2.0(iOS | macOS)

在RealityKit 2.0中,AR团队的工程师们为我们提供了一个.color属性,取代了.baseColor.tintColor。这两个属性在iOS 15中已被弃用。

iOS解决方案(RealityKit 2.0的颜色示例)

var material = SimpleMaterial()

material.color =  .init(tint: .red.withAlphaComponent(0.05), texture: nil)

material.baseColor       // deprecated in iOS 15
material.tintColor       // deprecated in iOS 15

enter image description here

iOS解决方案(RealityKit 2.0的纹理示例)

可以使用相同的初始化器来应用纹理:

material.color = try! .init(tint: .white.withAlphaComponent(0.9999),
                         texture: .init(.load(named: "mat.png", in: nil)))

特别注意“tint”乘数 - 如果您的纹理有透明部分,必须使用0.9999的值。
而且,您可以在这里找到如何设置PhysicallyBasedMaterial的透明度。


RealityKit(visionOS)

在 visionOS 的 RealityKit 中,有一个名为 OpacityComponent 的组件,其默认的 opacity 参数值为 1.0。

let model = ModelEntity(mesh: .generateSphere(radius: 0.1), 
                   materials: [SimpleMaterial(color: .red, 
                                         isMetallic: true)])

model.components[OpacityComponent.self] = .init(opacity: 0.5)

enter image description here


1
https://dev59.com/37joa4cB1Zd3GeqPCqsV - ColdLogic
如果在iOS上使用arkit时,使用没有纹理的简单材质,这根本不起作用。该材质是金属的,完全没有透明度。 - zackify
2
如果您在初始创建模型实体后更新网格大小,则不透明度无法工作。这是又一个RealityKit的错误,也是我认为您的解决方案不起作用的原因 :)已经为您修复了。 - zackify
1
@AndyJazz,你能帮忙看一下这个问题吗? https://dev59.com/PsPra4cB1Zd3GeqPWwV1 - elk_cloner
1
关于“球体留下的奇怪倒影”错误。我已经使用UIColor.clear和UIColor.clear.withAlphaComponent(0.0)来获取一个透明的ModelEntity水平面,但它从未完全透明,总是有这种“白色般”的面板颜色。当我使用UnlitMaterial时,它是一种“黑色般”的平面颜色。这似乎是一个错误。你知道任何解决方法吗(或者我应该发布另一个问题)? - Darkwonder
显示剩余8条评论

0
如果您希望为您的ModelEntity实现完全透明度,请使用以下代码:
    let invisibleMaterial = UnlitMaterial(color: .clear)
    let tapEntity = ModelEntity(mesh: replaceThisWithYourMesh, materials: [invisibleMaterial])

解释:

起初,我使用的是SimpleMaterial,但是没有任何解决方案能够使实体完全不可见。所以我查看了文档,发现还有另一种叫做UnlitMaterial的材质非常适合我的使用场景:

UnlitMaterial: 一种简单的材质,不会对场景中的光源做出反应。

另外,你也可以使用OcclusionMaterial,它也可以将对象隐藏在实体后面。


-2

我找到了几种方法来做到这一点。

  1. 没有动画,最简单的方法是使用 OcclusionMaterial()
let plane = ModelEntity(
                       mesh: .generatePlane(width: 0.1, depth: 0.1), 
                       materials: [OcculusionMaterial()]
            )

更改现有实体的不透明度:

plane.model?.materials = [OcclusionMaterial()]

带有动画(您可以根据需要调整这些片段):
var planeColor = UIColor.blue

func fadeOut() {
        runTimer(duration: 0.25) { (percentage) in
            let color = self.planeColor.withAlphaComponent(1 - percentage)
            var material: Material = SimpleMaterial(color: color, isMetallic: false)
            if percentage >= 0.9 {
                material = OcclusionMaterial()
            }
            self.plane.model?.materials = [material]
        }

}

func fadeIn() {
        runTimer(duration: 0.25) { (percentage) in
            let color = self.planeColor.withAlphaComponent(percentage)
            let material: Material = SimpleMaterial(color: color, isMetallic: false)

            self.plane.model?.materials = [material]
        }
}

func runTimer(duration: Double, completion: @escaping (_ percentage: CGFloat) -> Void) {
        let startTime = Date().timeIntervalSince1970
        let endTime = duration + startTime

        Timer.scheduledTimer(withTimeInterval: 1 / 60, repeats: true) { (timer) in
            let now = Date().timeIntervalSince1970

            if now > endTime {
                timer.invalidate()
                return
            }
            let percentage = CGFloat((now - startTime) / duration)
            completion(percentage)

        }
}

希望这能帮助到某个人)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接