삼각형 광선 충돌 아이디어
💡 삼각형이 이루는 평면에서 충돌한 지점이 삼각형 내부에 있는지 찾는다.
⇒ 삼각형의 특징: 하나의 평면 위에 존재한다.
1. 하나의 무한히 넓은 평면에서 광선이 충돌하는지 찾기
평면 법선 정규 백터를 이용한다. (n이라고 정의)
💡 삼각형을 이루는 벡터와 외적을 이용해 법선 벡터를 찾을 수 있다.
💡 충돌한 위치와, 삼각형을 이루는 점의 위치를 향하는 벡터와 평면의 법선 벡터를 내적하면 수직으로 0이 된다.
- 충돌한 위치는 다음과 같이 나타낼 수 있다.
- o ⇒ 광선의 원점
- d ⇒ 광선의 진행 방향
- t ⇒ 충돌 지점의 거리
- p = o + td (t는 스칼라, 나머지 변수 모두 벡터)
충돌 지점 거리 구하기 방정식
2. 이 평면과 광선이 만나는 곳이 삼각형 내부에 있는지 찾기
충돌 지점과 삼각형을 이루는 점을 이용해서 작은 삼각형 3가지를 만들 수 있으며, 작은 삼각형 3가지의 각각의 법선 벡터를 이용한다.
💡 삼각형 내부가 충돌 지점이면, 작은 삼각형 법선 벡터와 평면 법선 벡터 내적이 모두 0보다 크거나 같아야 한다.
- P를 기준으로 나눈 3가지 작은 삼각형과 외적을 이용한 작은 삼각형 법선 벡터를 구한다.
광선이 평면 내부에 있는 경우 예시 |
광선이 평면 외부에 있는 경우 예시 |
![]() |
![]() |
❗작은 삼각형 법선 벡터가 하나라도 다르면 충돌 지점이 삼각형 외부에 있는 것이다.
Metal에 적용
➕ 구를 그려낸 코드에서 확장해서 제작함
Raytracing으로 구 그리기
⛔ Metal Shader를 사용한 방법이 아니라 CPU를 사용해서 픽셀마다 색상을 직접 지정해 그리내는 렌더링 방법, GPU를 사용해서 나타낸 것은 아니다.필요 데이터 구조 Ray - 광선 광선의 시작점 광선의
arpple.tistory.com
필요 데이터 구조 - 추가된 것은 ⭐표
- Ray - 광선
- 광선의 시작점
- 광선의 방향
- Hit - 충돌시 유의미한 데이터
- 광선과 충돌 지점까지 거리
- 충돌한 위치
- 충돌한 위치의 법선 백터
- 충돌한 물체의 정보 (Obje 프로토콜)
- ⭐ Obje - 구, 삼각형 등 물체의 정보를 담는 프로토콜 (자바, C#은 인터페이스)
- color → 물체의 색
- Sphere: Obje - 구 (Obje 채택)
- 구의 중심 좌표
- 구의 반지름
- 구의 재질(색상)
- Triangle: Obje - 삼각형 (Obje 채택)
- 삼각형 꼭지점 벡터 3가지
- 삼각형의 재질(색상)
- Raytracer - 광 추적기
- 스크린의 사이즈
- ⭐ 물체들 정보 배열 (구, 삼각형 정보를 여기에 포함시킨다.)
- ⇒ 화면상에 픽셀들에서 광선을 내보내서 충돌하는 물체의 빛, 재질의 정보를 얻어오는 역할이다.
필요 메서드
- Obje
- checkRayCollision
특정 Ray가 본인에게 충돌하는지 확인하는 메서드
- checkRayCollision
- Triangle
- checkRayCollision⇒ intersectRayTriangle 메서드 호출
- ⇒ 특정 Ray가 본인에게 충돌하는지 확인하는 메서드
- intersectRayTriangle
- ⇒ 광선이 삼각형 내부에서 Hit 되는지 찾는 메서드
- RayTracer
- transformScreenToWorld
⇒ 2차원 스크린 좌표를 3차원 Metal 좌표계로 변환하는 메서드 - traceRay
⇒ ✅ findClosestCollision 메서드를 호출, Hit 정보를 가져옴
⇒ 광선이 물체(구)와 충돌한 후 충돌 정보(Hit)를 가져오는 메서드 - render
- transformScreenToWorld 수행
Metal 좌표계에서 스크린 좌표계에서 나오는 광선의 방향은 (0,0,1)로 통일함 - traceRay 수행
- 각각의 좌표에 맞게 색상 변환 후, Metal에서 Rendering 할 수 있는 Vertex 배열을 만들고 반환하는 메서드
- transformScreenToWorld 수행
- findClosestCollision
⇒ 여러 개의 물체 중 가장 가까운 거리에 존재하는 물체의 색상을 선택하는 메서드
- transformScreenToWorld
intersectRayTriangle 메서드
위에 삼각형 광선 충돌 알고리즘을 그대로 작성함
- 사용하는 메서드 파라미터
- rayStart: 광선의 시작위치
- rayDir: 광선의 방향
- faceNormalize: 평면의 법선 벡터 (Hit 데이터의 방향과 같다.) - inout을 사용해 상위 메서드로 값 넘긴다.
- t: 충돌지점, 광선과 평면이 만나는 거리의 크기(스칼라) - inout을 사용해 상위 메서드로 값 넘긴다.
- 메서드에서 검사 조건
- 광선의 방향과 평면의 법선 벡터 방향이 같으면
return false
- 광선의 방향 벡터와 평면의 각도 차이 0에 가까우면
return false
- t 값이 0보다 작으면 광선이 원하는 방향과 반대에서 진행됨
return false
- 작은 삼각형들의 법선 벡터와 평면의 법선 벡터가 반대 방향으로 진행되면
return false
- 광선의 방향과 평면의 법선 벡터 방향이 같으면
func intersectRayTriangle(_ rayStart:vf3,_ rayDir: vf3,point:inout vf3,fN faceNormalize:inout vf3,t:inout Float32) -> Bool{
// 평면의 법선 벡터
faceNormalize = simd_cross(v1 - v0, v2 - v0)
// 광선의 방향과 평면의 법선 벡터 방향이 같으면 return false
...
// 광선의 방향 벡터와 평면의 각도 차이 0에 가까우면 return false
...
t = (dot(v0,faceNormalize) - dot(rayStart,faceNormalize)) / dot(rayDir, faceNormalize)
// t 값이 0보다 작으면 광선이 원하는 방향과 반대에서 진행됨 return false
...
// 광선이 평면에 만나는 점의 위치 벡터
point = rayStart + rayDir * t
// 작은 삼각형들의 법선 벡터
let normal0 = simd_normalize(simd_cross(point - v2, v1 - v2))
let normal1 = simd_normalize(simd_cross(point - v0, v2 - v0))
let normal2 = simd_normalize(simd_cross(v1 - v0, point - v0))
// 광선이 삼각형 내부에 없으면 false
...
return true
}
findClosestCollision 메서드
RayTracer 객체 내부에 존재하는 물체들을 모두 순회해서 가장 가까운 충돌 지점을 갖는 색상을 반환
- 사용하는 메서드 파라미터
- ray: 광선
- color: 충돌한 물체의 색상 정보
func findClosestCollision(ray:inout Ray,color: inout vector_float3) -> Hit{
var closestD:Float32 = 1000.0
var closestHit = Hit(d: -1, point: .zero, normal: .zero)
for obj in objes{
let hit = obj.checkRayCollision(ray: &ray)
if hit.d < 0.0{ continue }
if hit.d < closestD {
closestD = hit.d
closestHit = hit
closestHit.obje = obj
color = obj.color
}
}
return closestHit
}
💡 구와 삼각형이 겹치면 화면과 더 가까운 물체 정보를 반환한다… → 삼각형이 구를 clip(잘라내지) 하지 못함
'Metal' 카테고리의 다른 글
무게 중심 좌표계, Barycentric coordinates (0) | 2024.04.30 |
---|---|
Perspective Projection & Phong Shading (0) | 2024.04.22 |
Raytracing으로 구 그리기 (0) | 2024.03.31 |
블룸 효과 만들기 with Metal (0) | 2024.03.11 |
댓글