항상 카메라를 향하는 객체를 GPU로 구현

2762 단어 ShaderUnity
2년 만에 Qiita에 게시.

3D 공간에서 카메라 앵글이 바뀌어도 항상 이쪽을 향하고 싶은 객체를 만들고 싶은 경우가 있습니다.

예를 들어, 2D 판에 텍스처를 붙이고, 그것을 항상 정면을 향하게 하는 것으로, 한 장 그림을 3D내에서 보이는, 라고 하는 테크닉(?)이 있습니다. 이런 평면을 판 다각형이나 빌보드 등이라고 부르기도 합니다.

before:


after:


이, 빌보드를 항상 카메라쪽으로 향하는 행위입니다만, 쉐이더로 간단하게 실장하는 방법을 알았으므로 소개해 보려고 생각합니다.

하는 방법은 매우 간단하고, 정점 쉐이더로, 클리핑 좌표를 계산하는 부분을 이하의 바꾸는 것 뿐입니다.
-OUT.vertex  = UnityObjectToClipPos(IN.vertex);  // 通常
+OUT.vertex = mul(UNITY_MATRIX_P, mul(UNITY_MATRIX_MV, float4(0, 0, 0, 1)) + float4(IN.vertex.x, IN.vertex.y, 0, 0)); // 常にカメラを向く

왜 이것으로 항상 방향이 일정해지는지 조금 해설해 보려고 합니다.

우선, 변경 전의, 언제나 하고 있는 좌표 변환의 UnityObjectToClipPos 입니다만,
최적화의 분기를 무시하면 대체로 이하와 같은 구현이 되어 있습니다.
mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));

음. 모델 변환, 뷰 변환, 프로젝션 변환의 행렬을 각각 곱하고 있을 뿐이지요.
(입력의 마지막 요소에 1.0 를 넣고 있는 것은, 3차원 벡터를 행렬로 좌표 변환할 때에, 이동(translate)도 허락하도록(듯이) 할 수 없다)

위에서 등장하는 좌표변환을 위한 행렬의 상수에 대해 조금만 되돌아보자.


상수
의미


UNITY_MATRIX_P
프로젝션 변환. 카메라가 비추는 영역을 화면에 투영하는 변환

UNITY_MATRIX_V
뷰 변환. 월드 좌표에서 카메라를 원점에 놓았을 때의 좌표계로의 변환

unity_ObjectToWorld
소위 모델 변환. 로컬 좌표를 월드 좌표로 변환. UNITY_MATRIX_M 라는 이름이 아닌 것이 이상하다(?)


이것을 근거로, 얼마 안되는, 카메라를 향하면서 클리핑 좌표로 변환하는 코드를 다시 한번 보겠습니다.
mul(UNITY_MATRIX_P, mul(UNITY_MATRIX_MV, float4(0, 0, 0, 1)) + float4(IN.vertex.x, IN.vertex.y, 0, 0))
UNITY_MATRIX_MV 는 V와 M의 행렬을 미리 CPU측에서 곱한 것입니다. 한 번 그리기의 변화 행렬은 불확실하기 때문입니다. 그래서, 실질, 이하의 코드와 동등합니다.
mul(UNITY_MATRIX_P, mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, float4(0, 0, 0, 1)) + float4(IN.vertex.x, IN.vertex.y, 0, 0)))

즉, float(0,0,0.1) 에 대해서 M과 V의 변환을 하는 것으로, 오브젝트의 어느 정점의 계산도 모두 뷰 좌표계(카메라를 원점으로 한 좌표계)에 있어서의 원점이라고 하는 것에 일단 해 버립니다. (모든 정점이 원점에 있으면, 회전도 거짓이 없습니다) 그 후, 뷰 좌표계에 있어서의 2D상에서 x와 y를 더하는 것으로, 뷰 좌표계내에서 정점의 위치를 ​​맞춥니다. 마지막으로 P 변환을 씌워 완성.

셰이더로 구현하는 이점



마지막으로, 이와 같이 방향을 바꾸는 처리를 셰이더로 구현하는 메리트에 대해입니다.
  • 총 계산비용 감소
  • GPU 측의 처리는 그다지 증가하지 않았다
  • CPU측에서 매 프레임 오브젝트의 방향을 계산하는 것도 가능합니다만, Unity의 경우는 메인 thread에 부하가 모이기 쉽기 때문에, 전체로서 리즈너블.

  • 게임을 재생하지 않아도, 씬 뷰 등 모든 표시로 이쪽을 향해 준다.
  • 좋은 웹페이지 즐겨찾기