2023. 1. 27. 17:59ㆍ컴퓨터 그래픽스
코딩 시작한 초창기에 컴퓨터 그래픽스 공부 하면서 구현한거라 가독성, 효율성, 일관성이 떨어지고 버그도 존재합니다.
양해부탁드립니다.
이미 c++로 구현 완료된 프로젝트 입니다.
OpenGL 로 구현된 부분도 있는데 이 부분은 윈도우 창 생성, 키보드 입력감지 등 기초적인 부분이고
렌더링 파이프 라인과 쉐이더에 관련된 내용은 API없이 직접 구현했습니다.
첫번째로 구성입니다.
main() 에서 윈도우 창 사이즈와 위치를 지정하고 생성합니다.
렌더링할 오브젝트들의 파일 정보를 읽고 display() 함수를 호출합니다.
호출된 display 함수는 640 * 480 배열(checkImage)에 저장된 색상을 받아 출력합니다.
checkImage 배열은 render() 함수 내에서 계산된 색상이 저장되어 있습니다.
프로젝트의 핵심부분인 render() 함수입니다.
world space, view space, projection space, screen space,
normal, texture mapping, shadow, R vector, light, z-buffer, back face culling,
multiple matrix, inverse matrix, 등등 기초적인 부분또한 직접 구현했습니다.
먼저 렌더링 파이프라인에 대한 설명입니다.
엣지 테이블이란 객체의 삼각형 정보를 저장하는 클래스입니다.
ET에 저장하기전 삼각형의 정보를 읽어 backFace() 함수를 통해 뒷면인 삼각형을 렌더링하지 않습니다.
뒷면으로 판단되지 않은 ET를 초기화하고
ET의 정보를 build 합니다.
buildEdgeTable()함수는 msh 파일을 읽어서 저장된 객체의 정보를 Y에대해 증가하는 값 즉 기울기들을 계산하고 윈도우창을 벗어나는 삼각형을 제거합니다.
ET는 아래와 같이 선언되어 있습니다.
ET의 정보를 채워 넣었으면
현재 렌더링 하는 삼각형의 색상을 계산하기위해 fill()함수를 호출합니다.
fill() 함수의 내부는 y의 개수만큼 반복문을 실행하여 ET를 복사하고 edge를 추가하고 y값을 벗어난 엣지를 제거하고 x값 기준으로 정렬하며, 깊이테스트를 통해 현재 렌더링 하는 삼각형의 픽셀당 색상을 계산하여 checkImage 배열에 저장합니다.
위와같은 렌더링 파이프 라인을 거쳐 텍스쳐 맵핑한 이미지입니다.
여기에 노말과 빛의 방향을 내적하여 빛의 값을 색상에 곱해주면
이런 렌더링 결과가 나오게 됩니다.
근데 제가 구한 파일에 normal, UV가 없어서 삼각형의 정보를 기반으로 계산했습니다.
normal은 삼각형의 정보를 외적해서 나온 벡터를 정규화 해 구한 face normal에서 보간법을 사용해 vertex normal을
계산 했습니다.
계산된 normal을 이미지로 출력한 결과입니다.
UV는 소의 좌표가 -1~1 까지인 메쉬이기 때문에 +1을 하고 X,Y의 최대값을 곱하여 생성했습니다.
똑같은 방식으로 빛이나 깊이또한 이미지로 출력이 가능합니다.
z test를 실행하지 않았을때 보이지 않아야 하는 부분들이 보입니다.
back face culling 또한 구현했는데 삼각형 하나를 그리고 카메라의 위치를 바꿔가면서 결과를 확인할 수 있습니다.
이미지는 생략합니다.
다음은 공간에 대한 구현입니다.
지금까지 본 소의 결과물은 모델 공간에서 월드, 카메라, 투영, 스크린 공간까지 다 곱한 매트릭스를 전부 vertex에 곱해서 스크린 공간까지 간 상태입니다.
이론적인 부분은 건너뛰고 하나만 예를 들어보자면
위와같은 공식을 사용하여 projection matrix를 계산했습니다.
위에 render()함수에서 객체의 vertex에 모든 matrix를 곱하는 부분을 확인할수 있습니다.
다음은 R vector 를 사용해 반사를 구현한 부분입니다.
반사를 구현하기 위해선 객체의 재질 정보가 필요하지만 위에서 말씀드렸듯 mesh 자체가
vertex와 face정보만으로 이루어져 있기때문에 재질에대한 계산은 추가하지 않았습니다.
사실 반사를 구현하기 위해서 matrix계산을 통해 카메라가 바라보는 방향을 반전해 배경을 이미지로 만들고 그 이미지를 텍스쳐로 사용해야 하지만 단순하게 아래의 이미지를 사용해 구현했습니다.
위의 구 이미지를 사용해 구현된 이미지 입니다.
다음은 그림자입니다.
그림자의 구현은 2pass로 나누었습니다.
첫번째 pass에선 카메라를 빛의 위치로 옮겨서 빛의 위치에서 가장 가까운 z값을 저장합니다.
두번째 pass에서 저장된 z값과 비교하여 그림자를 판단합니다.
그림자를 판단하기위해서 평면 객체를 추가했습니다.
그림자를 구현한 결과입니다.
객체나 빛의 움직임에 따라서 그림자가 움직이는 모습을 확인할수 있었습니다.
하나하나 전부다 구현했기 때문에 전부다 설명하기에는 너무 길어지기 때문에
전체 프로젝트는 아래 깃허브에 올려놓겠습니다.
https://github.com/yeedonghyun/rendering_Pipeline.git
GitHub - yeedonghyun/rendering_Pipeline
Contribute to yeedonghyun/rendering_Pipeline development by creating an account on GitHub.
github.com
감사합니다.