본문 바로가기
Metal

RayTracing으로 삼각형 그리기

by ARpple 2024. 4. 8.

삼각형 광선 충돌 아이디어

💡 삼각형이 이루는 평면에서 충돌한 지점이 삼각형 내부에 있는지 찾는다.
     ⇒ 삼각형의 특징: 하나의 평면 위에 존재한다.

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가 본인에게 충돌하는지 확인하는 메서드
    • Triangle
      • checkRayCollision⇒ intersectRayTriangle 메서드 호출
      • ⇒ 특정 Ray가 본인에게 충돌하는지 확인하는 메서드
      • intersectRayTriangle
      • ⇒ 광선이 삼각형 내부에서 Hit 되는지 찾는 메서드
    • RayTracer
      1. transformScreenToWorld
        ⇒ 2차원 스크린 좌표를 3차원 Metal 좌표계로 변환하는 메서드
      2. traceRay
        ✅ findClosestCollision 메서드를 호출, Hit 정보를 가져옴
        ⇒ 광선이 물체(구)와 충돌한 후 충돌 정보(Hit)를 가져오는 메서드
      3. render
        • transformScreenToWorld 수행
          Metal 좌표계에서 스크린 좌표계에서 나오는 광선의 방향은 (0,0,1)로 통일함
        • traceRay 수행
        • 각각의 좌표에 맞게 색상 변환 후, Metal에서 Rendering 할 수 있는 Vertex 배열을 만들고 반환하는 메서드
      4. findClosestCollision
        ⇒ 여러 개의 물체 중 가장 가까운 거리에 존재하는 물체의 색상을 선택하는 메서드
  •  

intersectRayTriangle 메서드

위에 삼각형 광선 충돌 알고리즘을 그대로 작성함

  • 사용하는 메서드 파라미터
    • rayStart: 광선의 시작위치
    • rayDir: 광선의 방향
    • faceNormalize: 평면의 법선 벡터 (Hit 데이터의 방향과 같다.) - inout을 사용해 상위 메서드로 값 넘긴다.
    • t: 충돌지점, 광선과 평면이 만나는 거리의 크기(스칼라) - inout을 사용해 상위 메서드로 값 넘긴다.
  • 메서드에서 검사 조건
    1. 광선의 방향과 평면의 법선 벡터 방향이 같으면 return false
    2. 광선의 방향 벡터와 평면의 각도 차이 0에 가까우면 return false
    3. t 값이 0보다 작으면 광선이 원하는 방향과 반대에서 진행됨 return false
    4. 작은 삼각형들의 법선 벡터와 평면의 법선 벡터가 반대 방향으로 진행되면 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

댓글