【iOS ARKit】光照效果--光源

光照

在现实世界中,光扮演了极其重要的角色,没有光万物将失去色彩,没有光世界将一片漆黑。在3D数字世界中亦是如此,3D数字世界本质上是一个使用数学精确描述的真实世界复本,光照计算是影响这个数字世界可信度的极其重要的因素。

光源

顾名思义,光源即是光的来源,常见的光源有阳光、月光、星光、灯光等。光的本质其实很复杂,它是一种电磁辐射但却有波粒二象性(我们不会深入研究光学,那将是一件非常复杂且枯燥的工作,在计算机图形学中,只需要了解一些简单的光学属性并应用)。在实时渲染时,通常把光源当成一个没有体积的点,用工表示由其发射光线的方向,使用辐照度(Irradiance)量化光照强度。对平行光而言,它的辐照度可以通过计算在垂直于L 的单位面积上单位时间内穿过的能量衡量。在图形学中考虑光照,我们只要想象光源会向空间中发射带有能量的光子,然后这些光子会与物体表面发生作用(反射、折射和吸收),最后的结果是我们看到物体的颜色和各种纹理。

RealityKit 中的光源

iOS Reality Kit 支持3种光源类型:平行光(DirectionalLight)、点光(PointLigbt)、聚光(SpotLight),分别同于模拟太阳光、普通灯泡类灯光、手电筒类灯光,组合使用这3种光源类型可以实现绝大部分光照效果道染需求。实际上,RealityRit还支持环境光,但 RealityKit 中的环境光照并不直接来自光源,而是使用IBL(Image Based Lighting,基于图像的光照)技术从背景图像中提取。

RealityKit 采用IBL 技术的初衷是增强3D虚拟元素的真实感,使用该技术可以从提供的环境资源貼因中提取环境信息及光照信息。环境资源贴图定义了AR环境中的大概颜色及明暗信息,利用这些信息,RealityKit 可以将其用于环境反射以增强虚拟元素的真实感和可信度。

使用环境资源贴图首先需要新建一个以.skybox为后辍的文件夹,并在该文件夹中放置使用的环境资源文件。环净资源贴图文件需为等距杜状投影环境图(经纬投影图)。將该文件实拖入 Xeode 工程文年写藏窗口中,在弹出的"选项"对 框中选择文件实引用(不要使用组),这样在工程编译译时 Xcode会自动打包该文件夹中所有的资源。

Reality Kit 环境资源贴图类型支持常见图像格式,如.png 和.jpg 格式图像文件,但获得表现力更丰富:颜色更饱满的照明和反射效果,建议使用.exr或,bdr 格式图像,这两种格式支持高动态范围,用于环境反射效果更出色。

在 RealityKit 中使用环境资源贴图只需要将贴图文件赋给 ARView. environment. background 属性,由干环境资源贴图放在特定的文件夹中,RealityKit 也提供了简洁的贴图加载方式,大大地简化了开发者的工作,典型代码如代码如下所示。

Swift 复制代码
arView.environment.background = .skybox(try! EnvironmentResource.load(named: "Space"))

平行光

平行光用于模拟在远处发射的光照效果,在Reality Kit中,使用平行光的代码如下:

Swift 复制代码
//
//  DirectionalLightView.swift
//  ARKitDeamo
//
//  Created by Zhaoquan on 2023/1/7.
//

import SwiftUI
import RealityKit
import ARKit

struct DirectionalLightView : View {
    var body: some View {
        return ARViewContainer11().edgesIgnoringSafeArea(.all)
    }
}

struct ARViewContainer11: UIViewRepresentable {
    
    func makeUIView(context: Context) -> ARView {
        
        let arView = ARView(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = .horizontal
        config.worldAlignment = .gravity
        arView.session.run(config, options:[ ])
        arView.session.delegate = arView
        arView.createPlane11()
        return arView
        
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
    }
}

var boxMesh = MeshResource.generateBox(size: 0.1)
var boxMaterial = SimpleMaterial(color:.white,isMetallic: false)
var boxEntity11 = ModelEntity(mesh:boxMesh,materials:[boxMaterial])

var planeMesh11 = MeshResource.generatePlane(width: 0.3,depth: 0.3)
var planeMaterial11 = SimpleMaterial(color:.white,isMetallic: false)
var planeEntity11 = ModelEntity(mesh:planeMesh11,materials:[planeMaterial11])

extension ARView {
    func createPlane11(){
        let planeAnchor = AnchorEntity(plane:.horizontal,classification: .any,minimumBounds: [0.3,0.3])
        planeAnchor.addChild(boxEntity11)
        var tf = boxEntity11.transform
        tf.translation = SIMD3(tf.translation.x,tf.translation.y + 0.06,tf.translation.z)
        boxEntity11.move(to: tf, relativeTo: nil)
        planeAnchor.addChild(planeEntity11)
        
        //添加平行光源
        let directionalLight = DirectionalLight()
        //光照强度
        directionalLight.light.intensity = 50000
        //光照颜色
        directionalLight.light.color = UIColor.red
        directionalLight.light.isRealWorldProxy = false
        
        directionalLight.look(at: [0, 0, 0], from: [0.01, 1, 0.01], relativeTo: nil)
        planeAnchor.addChild(directionalLight)
        self.scene.addAnchor(planeAnchor)
    }
}


#if DEBUG
struct DirectionalLightView_Previews : PreviewProvider {
    static var previews: some View {
        ARViewContainer11()
    }
}
#endif

对平行光而言,光源位置并不重要,重要的是光照方向,在实际开发中,经常使用平行光的look()方法设置光照方向。

点光

点光从光源位置向所有方向发射光线,当光线沿各方向传播时会出现衰减,在RealityKit 中使用 attenuationRadius 参数表示光线最大有效距离,即超出该距离后的物体无法被光源照射。使用点光时,不仅要指定光源位置,还需要指定衰减半径,典型的点光使用代码如代码如下。

Swift 复制代码
//
//  PointLightView.swift
//  ARKitDeamo
//
//  Created by Zhaoquan on 2023/1/7.
//

import SwiftUI
import RealityKit
import ARKit

struct PointLightView : View {
    var body: some View {
        return ARViewContainer12().edgesIgnoringSafeArea(.all)
    }
}

struct ARViewContainer12: UIViewRepresentable {
    
    func makeUIView(context: Context) -> ARView {
        
        let arView = ARView(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = .horizontal
        config.worldAlignment = .gravity
        arView.session.run(config, options:[ ])
        arView.session.delegate = arView
        arView.createPlane12()
        return arView
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
    }
}



extension ARView {
    func createPlane12(){
        let planeAnchor = AnchorEntity(plane:.horizontal,classification: .any,minimumBounds: [0.3,0.3])
        
        let planeMesh = MeshResource.generatePlane(width: 0.8,depth: 0.8)
        let planeMaterial = SimpleMaterial(color:.white,isMetallic: false)
        let planeEntity = ModelEntity(mesh:planeMesh,materials:[planeMaterial])
        planeAnchor.addChild(planeEntity)
        
        let boxMesh = MeshResource.generateBox(size: 0.1)
        let boxMaterial = SimpleMaterial(color:.white,isMetallic: false)
        let boxEntity = ModelEntity(mesh:boxMesh,materials:[boxMaterial])
        planeAnchor.addChild(boxEntity)
        //添加点光源
        let l = PointLight()
        l.light = PointLightComponent(color: .green, intensity: 5000, attenuationRadius: 0.5)
        l.position = [planeEntity.position.x , planeEntity.position.y + 0.5,planeEntity.position.z+0.2]
        l.move(to: l.transform, relativeTo: nil)
        
        let lightAnchor = AnchorEntity(world: l.position)
        lightAnchor.components.set(l.light)
        self.scene.addAnchor(lightAnchor)
        self.scene.addAnchor(planeAnchor)
    }
}


#if DEBUG
struct PointLightView_Previews : PreviewProvider {
    static var previews: some View {
        ARViewContainer12()
    }
}
#endif

聚光

聚光沿圆锥体发射光线,类似于手电筒的发光效果,其光线在传播时也会出现衰减,同时,在使用光需要指定圆锥角(innerAngleInDegrees)、聚光方向角(outerAngleInDegrees) 和光照方向,典型的聚光使用代码如下所示。

Swift 复制代码
//
//  SpotLightView.swift
//  ARKitDeamo
//
//  Created by Zhaoquan on 2023/1/7.
//


import SwiftUI
import RealityKit
import ARKit

struct SpotLightView : View {
    var body: some View {
        return ARViewContainer12().edgesIgnoringSafeArea(.all)
    }
}

struct ARViewContainer13: UIViewRepresentable {
    
    func makeUIView(context: Context) -> ARView {
        
        let arView = ARView(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = .horizontal
        config.worldAlignment = .gravity
        arView.session.run(config, options:[ ])
        arView.session.delegate = arView
        arView.createPlane13()
        return arView
        
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
    }
}
/*
class SpotLight: Entity, HasSpotLight {

    required init() {
        super.init()
        self.light = SpotLightComponent(color: .yellow,intensity: 50000, innerAngleInDegrees: 60,outerAngleInDegrees: 130,attenuationRadius: 5)
    }
}
 */

var planeMesh13 = MeshResource.generatePlane(width: 0.8,depth: 0.8)
var planeMaterial13 = SimpleMaterial(color:.white,isMetallic: false)
var planeEntity13 = ModelEntity(mesh:planeMesh13,materials:[planeMaterial13])

extension ARView : ARSessionDelegate{
    func createPlane13(){
        let planeAnchor = AnchorEntity(plane:.horizontal,classification: .any,minimumBounds: [0.3,0.3])
        planeAnchor.addChild(planeEntity13)
        let l = SpotLight()
        l.light = SpotLightComponent(color: .yellow, intensity: 5000, innerAngleInDegrees: 5, outerAngleInDegrees: 80, attenuationRadius: 2)
        l.position = [planeEntity13.position.x , planeEntity13.position.y + 0.1,planeEntity13.position.z+0.5]
        l.move(to: l.transform, relativeTo: nil)
        let lightAnchor = AnchorEntity(world: l.position)
        l.look(at: planeEntity13.position, from: l.position, relativeTo: nil)
        lightAnchor.components.set(l.light)
        self.scene.addAnchor(lightAnchor)
        self.scene.addAnchor(planeAnchor)
    }
}


#if DEBUG
struct SpotLightView_Previews : PreviewProvider {
    static var previews: some View {
        ARViewContainer13()
    }
}
#endif

利用光照可以在数字世界中模拟真实物体的照明效果,营造真实可信的虛实融合场景,但光照计算是一頂对资源消耗比较大的任务,场最中光源设置得越多,对性能消耗就越大,为提商应用性能,需要控制场果中的光源数量,或者使用顶渲染的纹理贴图替代实时光照计算。RealityKit 中各光源类型对资源消耗排序为:聚光>点光>平行光>环境光,聚光对性能消耗最大,需谨慎使用。

相关推荐
若水无华19 小时前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"20 小时前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy1 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克1 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨1 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆1 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂2 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T2 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa
struggle20252 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
Unlimitedz2 天前
iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)
ios·音视频