[Computer Graphics] Ray Tracing #1 (번역)

2023. 10. 11. 00:02Run/Computer Graphics

https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-ray-tracing

Ray Tracing 예시 코드를 찾아보던 중 해당 사이트를 알게 되었고, 한 번 이론을 훑어본 거로는 기억에 남지 않을 것 같아서 사이트에 적힌 이론 부문을 번역 및 정리(영어가 더 편한 경우 영어로 작성, 일부 내용은 생략)하고 예시 코드를 따라 작성해보고자 한다.

 

Keywords: ray-tracing, perspective projection, conductor, dielectric, forward tracing, backward tracing, shadow ray, primary ray, eye ray, path tracing, reflection, refraction, Appel, Whitted, index of refraction, Fresnel equation, transmission, recursive, recursion, depth, image file format

 

[ 0. How Does It Work? ]

이 강의를 시작하기 앞서 어떻게 3D scene으로 viewable 2D image를 만드는지 설명한다.

Ray Tracing은 physical phenomena를 simulating하는 가장 기본적인 알고리즘이다.

 

How Does an Image Get Created?

이미지를 생성하기 위해 가장 먼저 필요한 것은 2D surface이다. (이 surface는 point가 아니라 area여야 함)

이를 바탕으로 우리는 이미지를 apex(정점)가 eye 중앙에 위치해 있고 height가 line of sight과 평행한 피라미드 형태로 만든다. 이를 image plane이라고 한다.

Image plane은 컴퓨터 그래픽스의 개념이며, 우리는 3D scene을 project하기 위해 이를 2D surface로 사용할 것이다.

 

Perspective Projection

빈 canvas에 cube를 그린다고 하자.

Projection process를 묘사하기 가장 쉬운 방법은, 3D cube의 각 코너에서 eye로 선을 그리는 것이다.

물체의 형태를 canvas에 map out(배치) 하기 위해 각 line이 image plane의 surface와 intersect하는 곳에 point를 표시한다.

위 예시에서, c0은 cube의 코너에 위치해 있고 c1, c2, c3와 연결되어 있다.

이 4개의 점을 canvas에 project하여, c0', c1', c2', c3'를 얻을 수 있다.

만약 c0-c1이 edge라면 c0'-c1'도 line으로 그릴 수 있다.

이 과정을 모든 edge에 대해 반복하면 canvas에 cube를 그릴 수 있다.

이것이 perspective projection이다.

 

Scene에 있는 모든 object에 대해 이 과정을 반복하면 특정 시점에서 바라본 scene의 이미지가 생성된다.

 

Light and Color

이전 단계에서 outline을 그렸다면 이번 단계는 색을 더하는 과정이다.

 

Scene에서 object의 color와 brightness는 object의 material과 빛들이 interacting한 결과이다.

빛은 photons(electromagnetic particles)로 이루어져 있다.

즉, electric component와 magnetic component로 이루어져 있다.

Photon은 음파와 같이 energy와 oscillate를 가져 직선으로 이동한다.

Photon들이 object에 부딪히면, 3가지 현상이 일어날 수 있다. 1) absorbed 2) refleted 3) transmitted

Photon이 absorbed 되거나 reflected 되거나 transmitted 될 확률은 material마다 다르며,

이는 scene에서 object가 어떻게 보여질지를 결정한다.

만약 하나의 point를 비추는 100개의 photon이 있을 때 absorbed/reflected/transmitted 된 photon의 총합은 100개이다.

 

과학에서 material은 conductor(도체)와 dielectric(유전체)로 구별된다.

Dielectric으로는 glass, plastic, wood, water 등이 있다.

Dielectric은 transparent(투명)하거나 opaque(불투명)할 수 있다.

이 material은 electric insulator(절연체)의 속성을 가지고 있다.

모든 material은 특정 electromagnetic radiation에 투명하다. (ex. X-ray는 몸을 통과함)

 

Object는 composite material 또는 multi-layered material로 만들어질 수 있다.

예를 들어, opaque object(ex. wood)에 transparent coat of varnish(광택제)를 가질 수 있다.

 

Opaque하고 diffuse한 object를 고려해보자.

간단함을 위해 absorpton process가 object의 color에 영향을 준다고 가정한다.

White light는 red, blue, green photon들로 만들어져 있다.

만약 white light가 red object를 비춘다면 green, blue photon은 흡수되고 red photon은 반사될 것이다.

그리고 일부 red photon이 우리의 눈으로 들어와 우리는 object를 빨갛게 보게 된다.

 

빛이 비춰진 point는 모든 방향으로 light ray를 반사한다.

각 point에서 하나의 ray만이 눈에 수직으로 들어와 보이게 된다.

 

 

[ 1. Ray Tracing Algorithm ]

Ibn an-Haytham이 설명한 현상은 우리가 왜 object를 볼 수 있는지를 설명한다.

그의 설명을 바탕으로 2가지 사실을 말할 수 있다.

1) 빛이 없으면 우리는 아무것도 볼 수 없다.

2) 물체가 없으면 우리는 빛을 볼 수 없다.

 

Forward Tracing

Object로부터 반사되는 ray 중 일부만 눈에 도달한다.

 

한 번에 하나의 photon만 방출하는 light source가 있다고 하자.

방출된 photon은 object의 surface에 도달할 때까지 수직으로 이동한다.

Photon absorption을 무시하고 photon이 random direction으로 반사되었다고 하자.

만약 photon이 눈에 도달하면 우리는 photon이 random direction으로 반사되었음을 추측할 수 있다.

= if the photons hit the surface of our eye, we "see" the point wheere the photon was reflected from

 

이를 computer graphics에 적용해보자.

눈은 pixel로 구성된 image plane으로 대체된다.

방출된 photon은 하나의 pixel에 도달하고, 이는 그 point의  밝기를 높이게 된다. (greater than 0)

이 과정은 모든 pixel에 대해 적용되어 computer-generated image가 생성될 때까지 반복된다.

이 기술을 Forward Ray Tracing이라 부른다.

 

이 기술의 문제점은 다음과 같다.

우리는 반사된 photon이 항상 눈에 intersect 된다고 가정하였다.

실제로 ray는 모든 방향으로 반사되기 때문에 눈에 도달할 확률은 매우 낮다.

따라서 눈에 도달하도록 하기 위해 아주 많은 photon을 cast 해야한다.

 

우리는 빛의 photon을 scene으로 보내는 과정을 light ray를 object의 surface로 spraying하는 것처럼 시각화할 수 있다.

만약 spray의 밀도가 높지 않다면 일부 area는 균일하게 비춰지지 않는다.

 

Photon을 얼마나 쏘든 object를 photon으로 완전히 덮을 것이라고 보장할 수 없다.

이것이 Forward Ray Tracing의 주요 단점이다.

Object가 photon으로 완전히 덮였는지 확인하기 위해 렌더링될 때마다 이미지를 확인해야 한다.

또한, photon들을 생성하는 것은 문제가 되지 않지만 scene에서 photon의 모든 intersection을 찾는 것은 expensive하다.

 

Backward Tracing

Light source에서 receptor(eye)로 빛을 쏘는 것이 아니라 반대로 receptor에서 light source로 쏘는 방식이다.

이는 Forward tracing의 단점을 보완한다.

Simulation이 실제처럼 빠르고 완벽할 수 없기 때문에 눈에서 scene으로의 ray를 trace 해야한다.

만약 ray가 object에 도달하면 우리는 다른 ray(light ray or shadow ray)를 쏴 얼마나 많은 light을 받는지 알아낸다.

종종 이 light ray는 scene의 다른 object에 의해 방해를 받는데, 이는 original hit point가 그림자 안에 있음을 의미한다.

이런 이유로 shadow ray라고 부른다.

눈에서 scene으로 쏘는 ray는 primary ray, visibility ray, camera ray로 불린다.

 

Light이나 eye로부터 ray를 shooting 하는 이 방식은 path tracing이라고 불린다.

Ray tracing이라고도 할 수 있지만 path tracing이 이 방식을 더 잘 설명한다.

 

[ 2. Implementing the Ray Tracing Algorithm ]

Ray Tracing 알고리즘은 pixel로 구성된 image를 사용한다.

모든 pixel에 대해 scene으로 primary ray를 쏜다.

Primary ray의 방향은 눈에서 pixel 중앙으로 이어지는 직선으로부터 얻어진다.

Primary ray의 방향을 구하고 난 뒤에는, scene의 모든 object에 대해 primary ray와 intersect 하는지 확인한다.

Primary ray가 하나 이상의 object와 intersect하는 경우도 있다.

이 경우, 눈으로부터 intersection point가 가장 가까운 object를 선택한다.

 

그 다음 intersection point에서 light로 shadow ray를 쏜다.

 

이 ray가 light로 향하는 중에 object와 intersect 하지 않으면 hit point가 조명된다.

만약 어느 object와 intersect 하면 hit point에 그림자가 cast 된다.

 

이 과정을 모든 pixel에 대해 반복하면 2D 결과물을 얻을 수 있다.

 

pseudo code:

for (int j = 0; j < imageHeight; ++j) { 
    for (int i = 0; i < imageWidth; ++i) { 
    
        // compute primary ray direction
        Ray primRay; 
        computePrimRay(i, j, &primRay);
        
        // shoot primary ray in the scene and search for intersection
        Point pHit; 
        Normal nHit; 
        float minDist = INFINITY; 
        Object object = NULL; 
        
        for (int k = 0; k < objects.size(); ++k) { 
            if (Intersect(objects[k], primRay, &pHit, &nHit)) { 
                float distance = Distance(eyePosition, pHit); 
                if (distance < minDistance) {
                	// select object
                    object = objects[k]; 
                    minDistance = distance;  //update min distance 
                } 
            } 
        }
        
        if (object != NULL) { 
            // compute illumination
            Ray shadowRay; 
            shadowRay.direction = lightPosition - pHit; 
            bool isShadow = false; 
            for (int k = 0; k < objects.size(); ++k) { 
                if (Intersect(objects[k], shadowRay)) { 
                    isShadow = true; 
                    break; 
                } 
            } 
        }
        
        if (!isShadow) 
            pixels[i][j] = object->color * light.brightness; 
        else 
            pixels[i][j] = 0; 
    } 
}

 

렌더링 과정은 2개의 프로세스로 이루어져 있다.

1) determines if a point is visible at a particular pixel (visibility part)

2) shades that point (shading part)

두 프로세스 모두 expensive하고 time-consuming하다.

알고리즘은 elegant하고 powerful 하지만, 정확도를 위해 상당한 rendering time을 필요로 한다.

 

해당 방법을 처음 설명한 Arthur Appel은 렌더링 과정을 accelerate하기 위해 여러 paper을 publish 하였는데,

이 acceleration schemes들을 사용함으로써 Ray Tracing의 사용이 쉬워졌다.

It has been used in nearly every production rendering software.