
⛔ Metal Shader를 사용한 방법이 아니라 CPU를 사용해서 픽셀마다 색상을 직접 지정해 그리내는 렌더링 방법,
GPU를 사용해서 나타낸 것은 아니다.
필요 데이터 구조
- Ray - 광선
- 광선의 시작점
- 광선의 방향
- Hit - 충돌시 유의미한 데이터
- 광선과 충돌 지점까지 거리 (d)
- 충돌한 위치 (position)
- 충돌한 위치의 법선 백터 (normalize)
- Sphere - 구
- 구의 중심 좌표
- 구의 반지름
- 구의 재질(색상)
- Raytracer - 광 추적기
- 스크린의 사이즈
- 물체 (구 정보를 여기에 포함시킨다.)
- ⇒ 화면상에 픽셀들에서 광선을 내보내서 충돌하는 물체의 빛, 재질의 정보를 얻어오는 역할이다.
필요 메서드
- 구
- intersectRayCollision
특정 Ray가 본인에게 충돌하는지 확인하는 메서드
- intersectRayCollision
- RayTracer
- transformScreenToWorld ⇒ 2차원 스크린 좌표를 3차원 Metal 좌표계로 변환하는 메서드
- traceRay ⇒ 광선이 물체(구)와 충돌한 후 충돌 정보(Hit)를 가져오는 메서드
- render ⇒ 위에서 정의한 메서드들을 이용해서 Metal에서 Rendering 할 수 있는 Vertex 배열을 만들고 반환하는 메서드
- 스크린 좌표 정보들을 기반으로 전체 순회하며 로직 실행한다.
- transformScreenToWorld 수행 (Metal 좌표계에서 스크린 좌표계에서 나오는 광선의 방향은 (0,0,1)로 통일함)
- traceRay 수행
- 각각의 좌표에 맞게 색상 변환 후, Metal에서 Rendering 할 수 있는 Vertex 배열 반환
💡 traceRay 구현 아이디어: 구의 방정식과 직선의 방정식을 이용해 접점에서 Hit의 거리(d)를 찾자
Line–sphere intersection - Wikipedia
From Wikipedia, the free encyclopedia The three possible line-sphere intersections: 1. No intersection. 2. Point intersection. 3. Two point intersection. In analytic geometry, a line and a sphere can intersect in three ways: No intersection at all Intersec
en.wikipedia.org
- X는 Hit 위치⇒ C: 구의 중심, R: 구의 반지름⇒ O: 직선 시작점, D: 스칼라 직선 거리, U: 직선 방향(유닛 벡터)
- 직선의 방정식: X = O + d dot u
- 구의 방정식: (X - C)^2 = r^2
위에 Wikipedia 식 참고
d = -[u dot (o - c)] ± sqrt(Z)
Z = [u dot (o - c)]^2 - (|o-c|^2 - r^2)
- Z가 0보다 작다: 직선이 구와 만나지 않는다. -> 무시 or 배경처리...
- Z가 0이다: 직선이 구와 한 점에서 만난다.
- Z가 0보다 크다: 직선이 구와 두 점에서 만난다. (관통한다.)
✅ Hit의 법선 정규 백터 = normalize(Hit Position - 구의 중심 Position)
💡 Renderer 코드
func render() -> [Vertex]{
var vertices:[Vertex] = Array(repeating: .init(position: .zero, color: .zero), count: height * width)
for j in (0..<height){
for i in (0..<width){
let pixelPosWorld = self.transformScreenToWorld(posScreen: .init(x: Float(i), y: Float(j)))
let rayDir = vector_float3(0, 0, 1.0)
var ray = Ray(start: pixelPosWorld, dir: rayDir)
let positionColor = vector_float4(traceRay(ray: &ray), 1.0)
vertices[j * width + i] = Vertex(position: .init(x: pixelPosWorld.x, y: pixelPosWorld.y), color: positionColor)
}
}
return vertices
}
'Metal' 카테고리의 다른 글
무게 중심 좌표계, Barycentric coordinates (0) | 2024.04.30 |
---|---|
Perspective Projection & Phong Shading (0) | 2024.04.22 |
RayTracing으로 삼각형 그리기 (0) | 2024.04.08 |
블룸 효과 만들기 with Metal (0) | 2024.03.11 |
댓글