Machineboy空
RealityKit - Plane에 벡터이미지 texture 적용하기, UnlitMaterial, opacityThreshold 본문
언어/iOS
RealityKit - Plane에 벡터이미지 texture 적용하기, UnlitMaterial, opacityThreshold
안녕도라 2024. 11. 12. 21:47누끼 딴 사진을 3D 공간에 넣어 제스처를 적용하고 싶다!
- image로 texture만드는 방법
- texture를 투명하게 설정하는 방법 등을 소개할 예정이다!
2D 인듯 3D인 Plane의 특성
Plane 축을 잘 생각하며 코드를 짜야 한다!
기존 translation의 경우 좌우가 y축, 위아래가 z축과 연동되어 있기 때문에
Plane 생성 시, width와 depth 활용!
let planeMesh = MeshResource.generatePlane(width: 1, depth: 1)
UnlitMaterial의 Texture 속성 설정
- UnlitMaterial : A material that doesn’t respond to lights in the scene.
- material.color = .init(tint:.white)
- 정확히 기억은 안나는데 white가 0, black이 1 같은 그래픽스 개념이 있었는데 기억이 안난다..
- 여튼 .black으로 바꾸면 이미지가 나타나지 않음.
- material.opacityThreshold : 이걸 적용해 줘야 벡터 이미지 즉 투명 배경이 적용됌.
if let texture = try? TextureResource.load(named: imgName) {
var material = UnlitMaterial()
material.color = .init(tint: .white, texture: .init(texture))
material.opacityThreshold = 0.1
plane.model?.materials = [material]
}
샘플 코드
import SwiftUI
import ARKit
import RealityKit
struct ContentView: View {
@State private var imgName: String = ""
var body: some View {
VStack {
ARViewContainer(imgName: $imgName)
.ignoresSafeArea()
HStack(spacing: 10) {
ForEach(0..<4) { index in
Button(action: {
imgName = "pik\(index+1)"
}) {
Image("pik\(index+1)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.background(Color.teal)
.cornerRadius(10)
}
}
}
HStack(spacing: 10) {
ForEach(4..<7) { index in
Button(action: {
imgName = "pik\(index+1)"
}) {
Image("pik\(index+1)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.background(Color.teal)
.cornerRadius(10)
}
}
}
}
}
struct ARViewContainer: UIViewRepresentable {
@Binding var imgName: String
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: false)
arView.environment.background = .color(.white)
let emptyAnchor = AnchorEntity(world: [0, 0, 0])
context.coordinator.emptyAnchor = emptyAnchor
arView.scene.addAnchor(emptyAnchor)
let camera = PerspectiveCamera()
camera.position = [0, 5, 0]
camera.look(at: emptyAnchor.position, from: camera.position, relativeTo: nil)
let cameraAnchor = AnchorEntity(world: [0, 0, 0])
cameraAnchor.addChild(camera)
arView.scene.addAnchor(cameraAnchor)
context.coordinator.arView = arView
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {
context.coordinator.addDecoEntity(imgName: imgName)
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
}
class Coordinator: NSObject {
var arView: ARView?
var emptyAnchor: AnchorEntity?
func addDecoEntity(imgName: String) {
guard !imgName.isEmpty, let arView = arView else { return }
let planeMesh = MeshResource.generatePlane(width: 1, depth: 1)
let plane = ModelEntity(mesh: planeMesh)
if let texture = try? TextureResource.load(named: imgName) {
var material = UnlitMaterial()
material.color = .init(tint: .white, texture: .init(texture))
material.opacityThreshold = 0.1
plane.model?.materials = [material]
}
plane.generateCollisionShapes(recursive: true)
arView.installGestures([.all], for: plane)
emptyAnchor?.addChild(plane)
}
}
}
#Preview {
ContentView()
}
imgName값이 바뀌어야 updateUIView함수가 호출되기 때문에 동일한 버튼을 두 번 선택할 경우, 이미지가 두 개 생성되지 않는 문제가 있다.. 고쳐나갈 것!
+ 별 짓을 하다가 겨우 성공한 코드)
추후 변수명 수정 예정..
import SwiftUI
import ARKit
import RealityKit
import Combine
class CurrentImage: ObservableObject {
@Published var imgName: String = ""
}
struct ContentView: View {
@ObservedObject var currentImage = CurrentImage()
var body: some View {
VStack {
ARViewContainer(currentImage: currentImage)
.ignoresSafeArea()
HStack(spacing: 10) {
ForEach(0..<4) { index in
Button(action: {
currentImage.imgName = "pik\(index+1)"
}) {
Image("pik\(index+1)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.background(Color.teal)
.cornerRadius(10)
}
}
}
HStack(spacing: 10) {
ForEach(4..<7) { index in
Button(action: {
currentImage.imgName = "pik\(index+1)"
}) {
Image("pik\(index+1)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.background(Color.teal)
.cornerRadius(10)
}
}
}
}
}
struct ARViewContainer: UIViewRepresentable {
@ObservedObject var currentImage: CurrentImage
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: false)
arView.environment.background = .color(.white)
let emptyAnchor = AnchorEntity(world: [0, 0, 0])
context.coordinator.emptyAnchor = emptyAnchor
arView.scene.addAnchor(emptyAnchor)
let camera = PerspectiveCamera()
camera.position = [0, 5, 0]
camera.look(at: emptyAnchor.position, from: camera.position, relativeTo: nil)
let cameraAnchor = AnchorEntity(world: [0, 0, 0])
cameraAnchor.addChild(camera)
arView.scene.addAnchor(cameraAnchor)
context.coordinator.arView = arView
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(currentImage: currentImage)
}
}
class Coordinator: NSObject {
var arView: ARView?
var emptyAnchor: AnchorEntity?
var cancellable: AnyCancellable?
var currentImage: CurrentImage
init(currentImage: CurrentImage) {
self.currentImage = currentImage
super.init()
addDecoAndClear()
}
func addDecoAndClear() {
cancellable = currentImage.$imgName
.filter { !$0.isEmpty }
.sink { [weak self] imgName in
self?.addDecoEntity(imgName: imgName)
self?.currentImage.imgName = ""
}
}
func addDecoEntity(imgName: String) {
guard !imgName.isEmpty, let arView = arView else { return }
let planeMesh = MeshResource.generatePlane(width: 1, depth: 1)
let plane = ModelEntity(mesh: planeMesh)
if let texture = try? TextureResource.load(named: imgName) {
var material = UnlitMaterial()
material.color = .init(tint: .white, texture: .init(texture))
material.opacityThreshold = 0.1
plane.model?.materials = [material]
}
plane.generateCollisionShapes(recursive: true)
arView.installGestures([.all], for: plane)
emptyAnchor?.addChild(plane)
}
}
}
#Preview {
ContentView()
}
[결론]역시나 쉽게 해결될 줄 알았다... Coordinator자체를 Observable로 만드는 방법
import SwiftUI
import ARKit
import RealityKit
struct ContentView: View {
@StateObject private var coordinator = Coordinator() // Use Coordinator as an ObservableObject
var body: some View {
VStack {
ARViewContainer(coordinator: coordinator)
.ignoresSafeArea()
HStack(spacing: 10) {
ForEach(0..<4) { index in
Button(action: {
coordinator.addDecoEntity(imgName: "pik\(index+1)")
}) {
Image("pik\(index+1)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.background(Color.teal)
.cornerRadius(10)
}
}
}
HStack(spacing: 10) {
ForEach(4..<7) { index in
Button(action: {
coordinator.addDecoEntity(imgName: "pik\(index+1)")
}) {
Image("pik\(index+1)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.background(Color.teal)
.cornerRadius(10)
}
}
}
}
}
struct ARViewContainer: UIViewRepresentable {
@ObservedObject var coordinator: Coordinator
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: false)
arView.environment.background = .color(.white)
let emptyAnchor = AnchorEntity(world: [0, 0, 0])
coordinator.emptyAnchor = emptyAnchor
arView.scene.addAnchor(emptyAnchor)
let camera = PerspectiveCamera()
camera.position = [0, 5, 0]
camera.look(at: emptyAnchor.position, from: camera.position, relativeTo: nil)
let cameraAnchor = AnchorEntity(world: [0, 0, 0])
cameraAnchor.addChild(camera)
arView.scene.addAnchor(cameraAnchor)
coordinator.arView = arView
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
func makeCoordinator() -> Coordinator {
return coordinator
}
}
class Coordinator: NSObject, ObservableObject {
var arView: ARView?
var emptyAnchor: AnchorEntity?
func addDecoEntity(imgName: String) {
guard !imgName.isEmpty, let arView = arView else { return }
let planeMesh = MeshResource.generatePlane(width: 1, depth: 1)
let plane = ModelEntity(mesh: planeMesh)
if let texture = try? TextureResource.load(named: imgName) {
var material = UnlitMaterial()
material.color = .init(tint: .white, texture: .init(texture))
material.opacityThreshold = 0.1
plane.model?.materials = [material]
}
plane.generateCollisionShapes(recursive: true)
arView.installGestures([.all], for: plane)
emptyAnchor?.addChild(plane)
}
}
}
#Preview {
ContentView()
}
'언어 > iOS' 카테고리의 다른 글
ARView 스냅샷, 캡처 함수 (0) | 2024.12.17 |
---|---|
RealityKit - CollisionGroup과 Collision Filter를 알아보자! (0) | 2024.11.18 |
RealityKit - TextField에 입력한 글자를 3D Entity로 출력하기! (0) | 2024.11.11 |
RealityKit의 Gesture를 살펴보자 - Pan, Rotate (0) | 2024.11.11 |
RealityKit의 Gesture를 살펴보자 - Pinch, Scale (0) | 2024.11.11 |