[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 6회차 미션 시작합니다.


6회차 플레이어 카메라, 라이팅 시스템에 대한 교육 시간입니다.


일단 저번 시간에 이어 카메라 에디터 기능을 확장하는 것에 대한 내용입니다.
카메라 기능을 Scene Editor에서 수정할 수 있도록 Editor 기능을 확장하는 것입니다.

TopDownCamera 스크립트와 별개로 동작하는 스크립트가 필요합니다.
/Assets/ 위치에서 "Create C# Script" - "TopDownCamera_SceneEditor" Script를 만들어서 더블클릭하여 Editor로 진입합니다.

 


TopDownCamera_SceneEditor.cs

Editor를 확장하는 것이므로, Editor를 상속받는 클래스로 변경해주고, Start() Update() 함수는 제거합니다.
그리고 중요한 CustomEditor 임을 지정하는 코드를 추가합니다.

[CustomEditor(typeof(TopDownCamera))] //## 지정자를 지정해야지만 Scene View에 표시됨.
public class TopDownCamera_SceneEditor : Editor
{
}

 


필요한 변수들을 추가해줍니다.

private TopDownCamera targetCamera;


타겟 카메라를 받아오기 위해 OnInspectorGUI() 함수를 구현합니다.

public override void OnInspectorGUI()
{
  targetCamera = (TopDownCamera)target;
  base.OnInspectorGUI();
}

받아온 카메라에 대한 로직을 구현하기 위해서 OnSceneGUI() 함수를 구현합니다.

 


private void OnSceneGUI()
{
  if (!targetCamera || !targetCamera.target)
  {
    return;
  }
  
  Transform cameraTarget = targetCamera.target;
  Vector3 targetPosition = cameraTarget.position;
  targetPosition.y += targetCamera.lookAtHeight; // 타겟 카메라가 바라보는 위치값
    
  Handles.color = new Color(1f, 0f, 0f, 0.15f);
  Handles.DrawSolidDisc(targetPosition, Vector3.up, targetCamera.distance);
  
  Handles.color = new Color(0f 1f, 0f, 0.75f);
  Handles.DrawWireDisc(targetPosition, Vector3.up, targetCamera.distance);
  
  // Slider 기능 추가
  Handles.color = new Color(1f, 0f, 0f, 0.5f);
  targetCamera.distance = Handles.ScaleSlider(targetCamera.distance, targetPosition, -cameraTarget.forward, Quaternion.identity, targetCamera.distance, 0.1);

  targetCamera.distance = Mathf.Clamp(targetCamera.distance, 2f, float.MaxValue); // 최소, 최대 지정
  
  Handles.color = new Color(0f, 0f, 1f, 0.5f);
  targetCamera.height = Handles.ScaleSlider(targetCamera.height, targetPosition, Vector3.up, Quaternion.identity, targetCamera.height, 0.1f);

  targetCamera.height = Mathf.Clamp(targetCamera.height, 2f, float.MaxValue);


  GUIStyle labelStyle = new GUIStyle();
  labelStyle.fontSize = 15;
  labelStyle.normal.textColor = Color.white;
  
  labelStyle.alignment = TextAnchor.UpperCenter;

  Handles.Label(targetPosition + (-cameraTarget.forward * targetCamera.distance), "Distance", labelStyle);
 
  labelStyle.alignment = TextAnchor.MiddleRight;
  Handles.Label(targetPosition + (Vector3.up * targetCamera.height), "Height", labelStyle);
  
  targetCamera.HandleCamera(); // TopDownCamera 스크립트의 HandleCamera() 함수를 public으로 수정.
}

 


이렇게 ScaleSlider()를 사용하여 Camera distance와 height를 가변하는 기능으로 확장한 것입니다.
Debug 상태에서 지저분한 라인들을 표시하지 않기 위해 TopDownCamera 스크립트에서 Debug.DrawLine() 부분들을 주석처리하여 제거합니다.



플레이를 진행해보면 Scene View에서 Camera를 조절하여 Game View에 실시간으로 적용되어 구동되는 것을 확인할 수 있습니다. 대단하네요. 그냥 단순한 Object의 배치만이 아니라 각종 기능들을 추가함으로써 좀더 개발자 및 디자이너, 기획자에게 유용한 도구로 확장할 수가 있습니다.

캐릭터에 AI를 표시한다던지 게임요소들의 디버깅을 위해 정보를 표시한다던지 하는 형태로 확장할 수 있는 것이므로 꼭 카메라만이 아니라 여러 클래스에서 유용한 정보입니다.


 

 

 

 

 


이전까지 캐릭터 구현 및 카메라 구현등을 완료하였다면, 이제 게임 환경 관련 구축하는 내용입니다.

 

+ Lighting의 기본 개념 - 3D 게임중에 중요한 부분중 하나가 Lighting에 대한 이해와 활용입니다. 
+ Lighting Mapping 알아보기 - Lighting을 Texture에 입혀서 사용하는 방법입니다.
+ Light/Reflection Probe 알아보기 - 동적인 오브젝트에 대한 Global Illumination 처리.
+ 지형 시스템 알아보기 - 유니티에 내장된 Terrain System.
+ 네비게이션 시스템 알아보기 - 유니티에 내장된 Navigation System


라이팅이란 광원에서 반사된 빛이 물체에 반사되어 이것을 카메라로 보는 것입니다. 음영인 부분은 빛이 반사되지 않는 부분을 말합니다.

이와 관련하여 3D Graphics에서 보이는 색상을 계산하는 기본 공식입니다.


3D Graphics에서는 Ambient와 Diffuse Color 2가지를 조합하여 Final Color를 나타냅니다.
공식이 조금씩 다를 수는 있지만 보통 3D를 처리하는 OpenGL과 같은 Shader 기능에서는 비슷한 공식을 사용한다고 보면 됩니다.

이렇게 직접 계산하는 방식을 Direct Illumination이라고 합니다.
하지만 실제로는 한 오브젝트에 여러 Lighting이 적용되어 보여지는 경우가 많겠지요.

 

 

 


위와 같은 상태에서 오른쪽 구의 좌측면에는(마우스 위치) 아주 옅지만 붉은색 조명이 방사된 빛이 비치는 걸 볼 수 있습니다.

이러한 조명 처리를 Indirect Illumination이라고 합니다.

그리고 빛은 반사되는 물체에 따라 여러가지 반응을 나타내게 됩니다. 아래처럼 말이죠.


+ Transmission - 투영
+ Reflection - 반사
+ Refraction - 굴절
+ Diffraction - 회절
+ Adsorption - 흡착
+ Scattering - 산란

결국 Direct와 Indirect Lighting이 합쳐져서 Global Illumination이 만들어지게 되는 것이네요.
우리가 익히 알고 있는 자연현상이지만 이렇듯 하나하나 쪼개보니 이 또한 재미지네요.. 결국 이런 것이 물리학의 시작이겠지요.. ㅎㅎ



하지만 조명값을 실시간 계산하려면 부하가 많이 걸리므로 LightMapping 기능을 많이 사용하게 됩니다.

정적인 Object에 대한 Direct Lighting과 Indrect Lighting에 대한 Global Illumination 결과값을 Texture에 저장해 놓고 Object에 입혀서 사용하는 방식인 것입니다. 이러한 기능을 유니티에서는 "Baking"이라고 합니다.

유니티에서 베이킹은 시간이 걸리는 작업이라 준비된 프로젝트로 진행합니다.



유니티에서 Baking 작업하기.

일단 Global Illumination입니다.

+ Type: Directional
+ Color:
+ Mode: Mixed (Realtime | Mixed | Baked)
  '- Realtime - 실시간으로 모든 Object에 영향을 미칩니다. (Global Illumination Baking에는 포함되지 않음)
  '- Baked - 정적인 Object에만 영향을 미칩니다.
  '- Mixed - 정적인 Object에는 Lighting 기법을 사용하고, 동적인 Object에는 실시간 계산을 합니다.

 


Lighting 속성 설정 방법

[Window] - Rendering - Lighting Settings

+ Skybox Material: Default-Skybox
+ Sun Source: Directional Light (Light)
+ Source: Skybox
+ Realtime Global Illumination [ ] - 실시간 태양의 각도 변화를 따라 변화하는 것등을 할 수 있지만 많은 Performance를 요구하기 때문에 꼭 필요한 곳에서 잘 활용해야겠습니다.

 


기본적으로 태양광으로 설정되어 있으며, 던전 같은 Scene의 작업이라면 위의 값을 모두 삭제하여 "None" 처리를 하면 됩니다.

Lightmapper는 예전에는 Enlighten을 사용하였었는데 옆에 Deprecated 표시된 것처럼 향후에 사라질 예정입니다. 현시점에서의 유니티에서는 개발 상태에서 Progressive GPU로 테스트를 하고 Build할 때 Progressive CPU로 하는 방식을 추천하고 있습니다.

[Genrate Light]을 누르면 Baking을 준비하게 됩니다. 시간이 걸린 이후에 "Baked Lightmaps"에서 생성된 Texture를 볼 수 있으며, 이를 확인하기 위해 다음을 진행합니다.

 

 


[Scene View] - [Shaded] - Baked Lightmap


와우 예쁘네요 ^^~
이렇게 처리하여 결국 정적인 Object에서도 Global Illumination의 효과를 볼 수가 있게된 것입니다.

녹색, 빨강, 파랑, 흰색 등 오브젝트가 가진 색상에 따라 각각 처리되고 있습니다.



움직이는 캐릭터에는 어떤 영향을 미치게 될까요?


실행을 해보면 캐릭터는 주변 오브젝트들의 Lighting(Global Illumination)에 영향을 받지 않고, Direct Light에 설정한 직접 광원에만 영향을 받고 있습니다.

이걸 처리하기 위해 Light Probe를 처리해주어야 하고 다음 시간에 이어지겠습니다 ^^~

 

 


패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

+ Recent posts