그래픽스 기술

[Rasterization] 원 그리기와 vertex 2차원 변환

doyyy_0 2024. 10. 28. 15:49

이번에는 Rasterization을 통해 원을 그리고 그 원을 2차원변환(이동, 크기조절, 회전 등)을 조절해볼 것입니다.

고등학교 때 배웠던 간단한 수학 공식이 조금 필요한데요. 그 전에 Rasterization에서는 Raytracing과는 원을 그리는 방식이 다르다는 점을 이해해야합니다. Raysterizaion은 삼각형을 레스터좌표계에 투영해서 픽셀에 나타내는데요. 원 또한 수많은 삼각형을 통해 나타냅니다. 다음 그림과 같이 삼각형을 5개 그리면 5각형이 됩니다. 삼각형을 20개 정도 그리면 원에 가까워지겠죠? 

 

 

 

여기서 원을 그리는 원리를 알아볼 것입니다.

5개의 삼각형을 표현할 때 x,y좌표의 원점을 원의 중심이라고 생각합니다. 그리고 꼭짓점이 원점에서의 삼각형의 각도는 θ 입니다. 따라서 각 꼭짓점은 (r*cos (nθ), r*sin (nθ))로 나타낼 수 있습니다. 이 때 주의할 점이 우리는 삼각형의 시계방향을 앞면이라고 약속했습니다. 따라서 삼각형의 정점 v0,v1,v2는 반드시 시계방향으로 정의되어져야합니다.

  

 

 

다음은 원을 그리고, Update함수를 통해 원을 2차원변환 시켜볼 것입니다. 그 전에 회전 변환에 대해 이해해봅시다.  x,y는 Φ로 나타내고 변환된 x',y'는 θ+ Φ로 나타낸다고 했을 때, x',y'는 다음 그림과 같습니다.이 때, x', y'는 삼각함수 공식을 통해 , x,y, θ로 나타낼 수 있습니다. .

이것을 행렬 형태로 나타내면 다음과 같습니다.

 

또한 코드로 표현하면 다음과 같습니다.

vec3 RotateAboutZ(const vec3 &v, const float &theta) {

    return vec3(v.x * cos(theta) - v.y * sin(theta),
                v.x * sin(theta) + v.y * cos(theta), v.z);
}

 

 

 

이제 이것을 이용해 회전변환에 대해 이해해봅시다.

void Example::Update() {

    // 간단한 애니메이션 구현
    rasterization.Update();

    // 화면 지우기
    std::fill(pixels.begin(), pixels.end(),
              glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); // 검은 배경

    // 렌더링
    rasterization.Render(pixels);

    D3D11_MAPPED_SUBRESOURCE ms;
    deviceContext->Map(canvasTexture, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
    memcpy(ms.pData, pixels.data(), pixels.size() * sizeof(glm::vec4));
    deviceContext->Unmap(canvasTexture, NULL);
}

void Rasterization::Render(vector<vec4> &pixels) {
    //삼각형을 통해 원 구현하는 로직..
}

void Rasterization::Update() {
    // 애니메이션 구현

    for (size_t i = 0; i < circle.vertices.size(); i++) {
    
    	//첫번째 회전변환
        this->vertexBuffer[i] =
            RotateAboutZ(circle.vertices[i], this->rotation1);
		//크기 변환
        this->vertexBuffer[i] =
            this->vertexBuffer[i] * vec3(scaleX, scaleY, 1.0f);
		//첫번째 이동
        this->vertexBuffer[i] = this->vertexBuffer[i] + this->translation1;
		//두번째 회전변환
        this->vertexBuffer[i] =
            RotateAboutZ(this->vertexBuffer[i], this->rotation2);
		//두번째 이동
        this->vertexBuffer[i] = this->vertexBuffer[i] + this->translation2;
       

    }

}

Example 클라스의 Update는 매 프레임마다 Update가 한번 호출됩니다. 이 Update안의 함수 안에 Rasterization의 Update함수와 Render함수가 차례로 진행됩니다. Rasterization의 Update는 매 프레임마다 한번 호출이 되긴하는데 Example클라스의  Update함수에서 한번씩 호출하기 때문에 그렇게 됩니다. 즉 Rasterization의 Update는 변환 vertex에 대해서 작업을 하고 그 후에 Render함수에서 픽셀에 삼각형들을 그려주는 작업을 하게 됩니다.

 

여기서 가장 중요한 것은 Rasterization의 Update함수의 로직을 이해하는 것입니다. 이 로직을 이해하기 위해 다음을 이해해봅시다.

 

주의 : vertexBuffer에는 첫 회전변환시에만 circle의 vertex들을 받습니다.

 

처음 아무것도 움직이지 않았을 때의 그림은 다음과 같습니다.

 

 

첫번째 이동을 한 후 두번째 회전변환을 하였을 때, 다음 그림과 같이 원에서 떨어진 원의 vertex들이 원점을 기준으로 회전합니다.

 

 

 

이 때 여기서 첫 번째 회전 변환을 하면 어떻게 될까요? 

 

 

이번에는 원 자체가 회전을 하였습니다. 얼핏 봤을때 이미 떨어진 원이 원점을 중심으로 회전하고 원자체가 회전한 것처럼 보이지만, 사실 Rasterization의 Update문의 순서를 보면 원 자체를 회전시키고 첫번 째 이동 후에 그 원이 vertex들을 회전시킨 것입니다.  이 로직을 이해하고 있으면 다음을 이해할 수 있습니다.

 

여기서 다시 첫번째 이동 값 translation1을 줄이면 어떻게 될까요? 

 

 

이렇게 x,y축을 중심으로 이동하는게 아니라 원점으로 다가갑니다. 이유는 translation1이 진행이 되고, rotation2이 진행이 되기 때문입니다. 

 

 

강의 및 코드 출처 :

https://honglab.co.kr/courses/graphicspt2

 

HongLab 홍정모 연구소

홍정모의 컴퓨터 그래픽스 새싹코스

honglab.co.kr