쉐이더 프로그래밍 (Shader Programming) - NabiEILAB/ProjectionMappingSimulator GitHub Wiki

쉐이더와 프로젝션 맵핑 (Shader and projection mapping)

비디오 영상 텍스쳐를 3D 모델링의 어느 부분에 텍스쳐링을 할지
연산을 해주는 쉐이더를 통해 프로젝션 맵핑을 구현하였다.

openFrameworks에서의 쉐이더 (Shader in openFrameworks)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot01.png
openFrameworks에서 쉐이더를 이용하려면 동일한 이름을 가진
vert,frag 확장자명을 가진 텍스트파일을 코드내에서 ofShader 클래스로 생성후
load()함수를 이용하면 프로그램내에서 이용할 수 있다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot02.png
쉐이더를 이용한 상태로 렌더링을 하려면 ofShader의 begin()과 end()함수
호출부 사이에 draw()함수와 같은 모델링을 렌더링하는 함수를 호출하면
쉐이더가 적용된 채로 렌더링 된다.

개념 (Concept)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot03.png
openFrameworks 자체적으로 OpenGL과 GLSL 쉐이더 언어를 제공한다.
제공되는 GLSL 기반의 쉐이더 파일을 작성하여 본 코드내에서
상단의 그림과 같이 빔 프로젝터와 3D 모델링의 정보를 쉐이더 파일내에서
이용하여 비디오 텍스쳐가 입혀질 위치를 결정하여 가상공간에서 프로젝션 맵핑을 구현하였다.
쉐이더는 크게 3가지의 기법을 혼합하여 작성하였다.

  • Projective Texturing 쉐이더
  • Shadow Mapping 쉐이더
  • Point Lighting 쉐이더

쉐이더 파일이 존재하는곳은 다음과 같다.

  • (프로젝트 디렉토리)/bin/data/TextureProjection.vert
  • (프로젝트 디렉토리)/bin/data/TextureProjection.frag
  • (프로젝트 디렉토리)/bin/data/DepthStoring.vert
  • (프로젝트 디렉토리)/bin/data/DepthStoring.frag
    그 외의 쉐이더 파일은 프로젝션 맵핑이 아닌 단순한 광원 연산만을 해준다.

Projective Texturing 쉐이더 (Projective Texturing shader)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot04.png
링크 9.3 참조(http://developer.download.nvidia.com/CgTutorial/cg_tutorial_chapter09.html)
Projective Texturing은 상단의 그림처럼 마치 빔 프로젝터를 이용하여
물체에 비춘것과 같이 텍스쳐 좌표를 연산해주는 쉐이더 기법이다.
프로젝션 맵핑이라는 개념에 가장 핵심적인 형태로 연산을 해주는 중요한 연산을 담당하였다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot05.png
Projective Texturing의 공식과 같은 연산이 쉐이더 내에서 이루어지면
텍스쳐가 최종적으로 입혀질 좌표인 s,t,r,q를 얻어낼 수 있다. 이를 얻어내기 위해
5가지의 행렬이 필요하다. Modelign Matrix와 x,y,z,w는 3D 모델링 자체적으로 쉽게 구할 수 있다.
그러나 나머지 3가지의 행렬은 빔 프로젝터 오브젝트의 정보를 토대로 직접 연산하여야 한다.

Light View(look at) 행렬 (Light view matrix)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot06.png
openFrameworks 자체적으로 Look at 행렬을 연산해주는 makeLookAtViewMatrix()라는 함수가 존재한다.
해당 함수의 파라미터로 3가지의 ofVec3f 형태의 3가지의 좌표정보가 필요하다.

  • projectorPos
    빔 프로젝터의 현재 좌표위치
  • projectorTarget
    빔 프로젝터가 바라보는 직선상의 임의의 좌표
  • projectorUp
    빔 프로젝터의 위,아래 개념이 어떤 방향인지 알려주는 벡터

Light Frustum(projection) 행렬 (Light Frustum matrix)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot07.png
openFrameworks 자체적으로 Perspective 성격의 행렬인 projection 행렬을 연산해주는 newPerspectiveMatrix()라는 함수가 존재한다.
해당 함수의 파라미터로 4가지의 정보가 필요하다.

  • fov
    Field of view 혹은 Angle of view의 의미로 본 프로그램에서는
    빔 프로젝터가 투사할 영상의 크기를 의미한다.
    2 x atan(영상의 세로길이 / (2 x 빔 프로젝터와 최대 투사영역과의 거리)) x RAD2DEG라는 공식으로 얻을 수 있다.
  • aspect
    영상의 종횡비를 의미한다.
    (영상의 가로/영상의 세로)
  • distance / 1000
    빔 프로젝터와 최대 투사영역과의 거리(-3000과 빔 프로젝터의 z값의 차)를 단순히 1000으로 나눈 값
  • distance
    빔 프로젝터와 최대 투사영역과의 거리(-3000과 빔 프로젝터의 z값의 차)

bias 행렬 (bias matrix)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot08.png
연산 결과값을 0 ~ 1사이로 정규화시켜주는 행렬로 단순히 0.5씩 scale하고 0.5씩 translate하는 매우 간단한 행렬이다. 그러나 openFrameworks 자체적인 설계로 인해 공식과는 약간의 차이가 있다.

  • y좌표 scale을 0.5가 아닌 -0.5로 한다.
    openFrameworks는 좌표의 시작점이 좌측 하단이기 때문(원래 공식은 좌측상단 기준)
  • 추가적으로 x좌표를 영상의 가로(width)만큼 y좌표를 영상의 세로(heigh)만큼 scale 해준다.
    영상이 계속 빔 프로젝터가 바라보는 위치로 고정시켜주기 위함

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot09.png
구해낸 모든 행렬을 곱한 projectorMatrix를 쉐이더로 넘기면 된다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot10.png
그림과 같이 프로젝터의 위치로부터 모델링과의 위치관계를 고려해
마치 빔 프로젝터를 통해 영상을 투사한것 같은 결과를 나타내게 된다.
그러나, 영상이 입혀져선 안되는 위치의 경우도 그대로 입혀지는 문제로 인해
Projective Texturing 만으로는 올바른 결과를 나타낼 수 없다.

관련 레퍼런스 (References)

http://prog3.com/sbdm/blog/pizi0475/article/details/7932924
http://diehard98.tistory.com/entry/Projective-Texture-Mapping-with-OpenGL-GLSL
https://forum.openframeworks.cc/t/projective-texture-mapping-glsl/23677

Shadow Mapping 쉐이더 (Shadow Mapping shader)

앞서 Projective Texturing 만으로는 프로젝터가 바라보는 위치의 모델링의 모든 텍스쳐좌표에
영상 텍스쳐를 입힌다는 단점이 있다. 이를 해결하기 위해 모델링의 모든 텍스쳐 좌표의 z값들을 비교하여
그림자 영역과 텍스쳐 영역으로 판별해주는 Shadow Mapping 기법을 이용한다.
텍스쳐 좌표의 z값(깊이)를 저장해놓은 메모리를 이용하기 때문에
Depth Buffered Shadow(깊이 버퍼 그림자)기법이라고도 불린다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot11.png
쉐이더 이용 개요를 다시 세분화 해보면 쉐이더를 크게 2가지로 나누어 이용한다.
첫번째로 우선 모델링의 텍스쳐 좌표의 z값을 저장한 Shadow Map을 작성하기 위해
Shadow Mapping Shader를 이용해 렌더링을 실시한다( = First Pass Rendering)
그 뒤, 작성된 Shadow Map을 두번째 쉐이더로 넘겨주어 두번째 쉐이더에서는
받아낸 Shadow Map을 이용하여 해당 좌표가 텍스쳐 영역인지 그림자 영역인지 판별하여
텍스쳐를 입힌다.(= Second Pass Rendering)
렌더링을 총 2번하기 때문에 2-Pass Rendering이라고도 불린다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot12.png
Shadow Map은 텍스쳐의 형태로 이용해야한다. 이를 위해 Shadow Map은 각 빔 프로젝터 오브젝트마다
개별로 하나씩 FBO의 형태로 저장해두어 이용한다. openFrameworks 자체적으로 모델링의 텍스쳐 좌표의
z값을 저장하게끔 FBO를 생성할 수 있다. Shadow Mapping Shader는 코드상으로는 아무런 작업도 시행하지 않는다.
단순히 빔 프로젝터의 시점에서 모델링을 렌더링한것을 FBO에 저장하는 작업만 수행한다.
만든 Shadow Map을 앞서 만들었던 Projective Texturing Shader로 넘겨서 그림자 판별후 텍스쳐를 입힌다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot13.png
연산 결과로 그림자 영역인 부분은 음영처리가 되어 의도한 결과가 나왔으나
오히려 텍스쳐 영역이 그림자 영역과 뒤섞인듯한 결과를 나타내었다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot14.png
렌더링을 진행하는 시점이 일직선이 아닌 경우에 발생하는 문제로 z-fighting이라고도 불린다.
단순히 그림자 판별식에서 약간의 허용 오차범위를 설정하면 해결할 수 있다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot15.png
그림자의 경계부분이 약간 투박한 감이 있으나 의도한 결과를 나타낸다.

관련 레퍼런스 (References)

http://www.fabiensanglard.net/shadowmappingVSM/
http://www.sunandblackcat.com/tipFullView.php?l=eng&topicid=34
http://www.opengl-tutorial.org/kr/intermediate-tutorials/tutorial-16-shadow-mapping/

Point Lighting 쉐이더 (Point Lighting shader)

빔 프로젝터와의 거리에 관계없이 모든 모델링에 입혀지는 텍스쳐의 밝기값이
일정하다는 단점이 있다. 이를 해결하기 위해 쉐이더중 광원 맵핑을 구현하였으며
광원 맵핑중 Point Lighting(점광원) 기법을 이용하였다.
기존의 Projective Texturing이 쓰인 쉐이더 파일과 동일한 곳에서
쓰였으며 2가지 기법을 모두 혼합하여 사용한다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot16.png
프로젝터와 텍스쳐 좌표간의 거리를 고려하여 일정 거리에 따라 텍스쳐 밝기값을 늘리거나 줄이는 형태로
구현을 하였다.

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot17.png
거리에 따라 텍스쳐의 밝기값이 다르게 처리되는것을 확인할 수 있다.

관련 레퍼런스 (References)

http://ogldev.atspace.co.uk/www/tutorial20/tutorial20.html
https://www.tomdalling.com/blog/modern-opengl/07-more-lighting-ambient-specular-attenuation-gamma/
http://www.gisdeveloper.co.kr/?p=583

그 외 쉐이더 (Other shaders)

https://github.com/NabiEILAB/ProjectorSimulator/blob/master/wiki/ShaderProgramming/screenshot18.png

  • modelingBasicShader
  • projectorTextureShader
    위 2개의 쉐이더는 단순히 불러온 모델링과 빔 프로젝터의 모델링에 단순 광원질감을 표현하는 쉐이더로 프로젝션 맵핑과는 관계가 없다.