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 也提供了简洁的贴图加载方式,大大地简化了开发者的工作,典型代码如代码如下所示。
1 | arView . environment . background = . skybox ( try ! EnvironmentResource . load ( named : "Space" )) |
平行光用于模拟在远处发射的光照效果,在Reality Kit中,使用平行光的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | // // 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 参数表示光线最大有效距离,即超出该距离后的物体无法被光源照射。使用点光时,不仅要指定光源位置,还需要指定衰减半径,典型的点光使用代码如代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | // // 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) 和光照方向,典型的聚光使用代码如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | // // 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 中各光源类型对资源消耗排序为:聚光>点光>平行光>环境光,聚光对性能消耗最大,需谨慎使用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具