반응형

해당 포스트는 Cell Shading을 그리기 위한 Engine & Project와 Asset 설정에 대한 글이다.

 

Cell Shading의 특징은 일반적인 Light와는 다르게 Light의 표현이 고정되어 있다.

즉, 그라데이션으로 그림자가 지지 않는다는 뜻이다.

때문에 참고 링크에서도 Light를 표현할 곡선을 에셋으로 만들어두고 진행한다.

 

Engine/Contens/CellShading 폴더를 만들어, Linear Color Curve 에셋을 추가한다.

Cell Shading Linear Color Curve

그리고 Curve Atlas를 추가하여, Gradient Curves에 해당 Curve를 추가해준다.

Cell Shading Curve Atlas

이제 설정한 Asset을 어디서나 사용하기 위한 설정이 필요하다.

 

Project Settings에서 설정 가능하도록 DeveloperSettings Class를 상속받아 만들고, 

Subsystem을 사용하여 Global 하게 접근하도록 설정한다.

Cell Shading 관련 작업이 Engine에 추가되어 있으므로, Engine에 추가하는 것이 용이하다.

// CellShadingSettings.h :: Create
#pragma once

#include "CoreMinimal.h"
#include "Engine/DeveloperSettings.h"
#include "CellShadingSettings.generated.h"

UCLASS(config = Game, defaultconfig)
class UCellShadingSettings : public UDeveloperSettings
{
	GENERATED_UCLASS_BODY()

public:
	UPROPERTY(config, EditAnywhere, Category = General, meta = (ConfigRestartRequired = false))
	TSoftObjectPtr<UCurveLinearColorAtlas> CellShadingCurveAtlas;
};


// CellShadingSettings.cpp :: Create
#include "CellShadingSettings.h"

UCellShadingSettings::UCellShadingSettings(const FObjectInitializer& ObjectInitializer)
	: UObject(ObjectInitializer)
{

}

 

CellShadingSettings 클래스를 Engine Config로 설정해두었으므로, Project의 BaseEngine.ini 파일을 설정한다.

// BaseEngine.ini

~~ ... ~~

[/Script/Engine.CellShadingSettings]
CellShadingCurveAtlas=/Engine/CellShading/CurveAtlas_CellShading.CurveAtlas_CellShading

 

위의 작업을 진행하면 Project Settings를 통해 설정된 내용을 확인할 수 있다.

Project Settings

Engine에 모든 설정을 해두었기 때문에, Engine Subsystem을 상속받아 Global하게 사용할 수 있도록 설정한다.

// CellShadingSubsystem.h :: Create
#pragma once

#include "CoreMinimal.h"
#include "Subsystems/EngineSubsystem.h"
#include "CellShadingSubsystem.generated.h"

class UCurveLinearColorAtlas;

UCLASS()
class ENGINE_API UCellShadingSubsystem : public UEngineSubsystem
{
	GENERATED_BODY()

public:
	virtual void Initialize(FSubsystemCollectionBase& Collection) override;
	virtual void Deinitialize() override;

public:
	UCurveLinearColorAtlas* GetCellShadingCurveAtlas() const { return CellShadingCurveAtlas.Get(); }
	void UpdateCellShadingCurveAtlas(UCurveLinearColorAtlas* Atlas) { CellShadingCurveAtlas = Atlas; }

private:
	UPROPERTY()
	TObjectPtr<UCurveLinearColorAtlas> CellShadingCurveAtlas;

};


// CellShadingSubsystem.cpp :: Create
#include "CellShading/CellShadingSubsystem.h"
#include "CellShading/CellShadingSettings.h"
#include "Curves/CurveLinearColorAtlas.h"

void UCellShadingSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);

	if ( const UCellShadingSettings* CellShadingSettings = GetDefault<UCellShadingSettings>() )
	{
		CellShadingCurveAtlas = CellShadingSettings->CellShadingCurveAtlas.LoadSynchronous();

		ensureMsgf(CellShadingCurveAtlas, TEXT("Fail to load [UCellShadingSubsystem::Initialize]'s CellShadingCurveAtlas"));
	}
}

void UCellShadingSubsystem::Deinitialize()
{
	Super::Deinitialize();

	CellShadingCurveAtlas = nullptr;
}

Cell Shading Default Object를 통해, BaseEngine.ini에서 설정한 Asset을 가져온다.

Engine Subsystem은 Engine이 실행되었을 때, Instance로 존재하기 때문에 Subsystem 내에서 변수로 Global하게 사용하는 것이 가능하다.

GameSingleton & GameInsatance의 개념과 유사하다.

 

기본적으로는 BaseEngin.ini을 통해서만 설정하는 것을 원하지만, Project Settings을 통해서도 변경이 가능하기 때문에, CellShadingSettings Class에서 값이 변경되었을 때, CellShadingSubsystem의 값도 함께 변경되도록 작업해주어야 한다.

// CellShadingSettings.h
class UCellShadingSettings : public UDeveloperSettings
{
~~ ... ~~

#if WITH_EDITOR
protected:
	virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

~~ ... ~~
}


// CellShadingSettings.cpp
~~ ... ~~
#if WITH_EDITOR
void UCellShadingSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	if ( PropertyChangedEvent.Property )
	{
		if ( PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UCellShadingSettings, CellShadingCurveAtlas) )
		{
			if ( UCurveLinearColorAtlas* ColorAtlas = CellShadingCurveAtlas.LoadSynchronous() )
			{
				if ( UCellShadingSubsystem* CellShadingSubsystem = GEngine->GetEngineSubsystem<UCellShadingSubsystem>() )
				{
					CellShadingSubsystem->UpdateCellShadingCurveAtlas(ColorAtlas);
				}
			}
		}
	}
}
#endif
반응형

+ Recent posts