본문으로 건너뛰기

SDEF 지원

이 섹션에서는 구면 변형(SDEF)을 사용하는 MMD 모델을 올바르게 로드하는 방법을 설명합니다.

SDEF란 무엇인가?

일반적으로 스키닝에는 **선형 블렌드 스키닝(Linear Blend Skinning, LBS)**이 사용됩니다.

이 메서드는 각 버텍스가 여러 본의 영향을 받을 때, 각 본의 영향력 가중치에 기반하여 선형 보간법을 통해 버텍스 위치를 결정합니다.

이 메서드는 간단하고 빠르지만, 캔디 래퍼 아티팩트와 같은 문제를 일으킬 수 있습니다.

**구면 변형(Spherical Deformation, SDEF)**은 이러한 문제를 해결하기 위해 제안된 방법입니다. SDEF는 버텍스 위치를 결정할 때 선형 보간 대신 구면 보간을 사용하여 버텍스 위치를 더 자연스럽게 보정합니다.

babylon-mmd에서의 SDEF 지원

많은 MMD 모델이 SDEF를 사용합니다. 반면에 대부분의 현대 소프트웨어는 LBS만 지원합니다.

MMD 모델 사양과 MMD가 SDEF를 지원하기 때문에, babylon-mmd는 MMD의 동작과 동일한 결과를 얻기 위해 SDEF를 지원합니다.

Babylon.js 또한 SDEF를 지원하지 않기 때문에, babylon-mmd는 다소 비관행적인 방법을 사용합니다.

SDEF를 처리하기 위한 네 가지 옵션이 제공됩니다.

옵션 1: LBS 사용하기

SDEF 스키닝을 사용하는 모델도 LBS로 렌더링할 수 있습니다.

이 방법은 Babylon.js의 기본 스키닝 메서드를 그대로 사용하기 때문에 호환성이 가장 높습니다.

모델을 로드할 때 pluginOptions.mmdmodel.useSdef 값을 false로 설정하기만 하면 LBS 방법을 사용할 수 있습니다. (기본값은 true입니다.)

const assetContainer: AssetContainer = await LoadAssetContainerAsync(
modelFileOrUrl,
scene,
{
pluginOptions: {
mmdmodel: {
useSdef: false
}
}
}
);

하지만 다음과 같은 아티팩트가 발생할 수 있습니다.

이 비디오는 SDEF를 사용하는 MMD 모델을 LBS로 렌더링한 결과를 보여줍니다. 모델: YYB式初音ミク_10th_v1.02 by SANMUYYB

옵션 2: MmdStandardMaterial에만 SDEF 적용하기

MmdStandardMaterial을 사용하여 렌더링할 때 SDEF를 적용할 수 있습니다.

MmdStandardMaterial은 babylon-mmd에서 제공하는 MMD 모델 렌더링을 위한 머티리얼입니다. 이 머티리얼은 MaterialPlugin을 사용하여 Babylon.js의 StandardMaterial을 수정하여 만들어졌습니다. MmdStandardMaterial은 다양한 MMD 머티리얼 속성을 제공하고 SDEF를 지원합니다.

이 머티리얼을 사용하여 MMD 모델을 로드하고 SDEF를 적용하려면 다음과 같이 로드하세요:

const assetContainer: AssetContainer = await LoadAssetContainerAsync(
modelFileOrUrl,
scene,
{
pluginOptions: {
mmdmodel: {
materialBuilder: new MmdStandardMaterialBuilder(),
useSdef: true
}
}
}
);

이 머티리얼을 사용하면 SDEF 적용이 가능하지만, 머티리얼을 제외한 렌더 파이프라인의 나머지 부분에는 영향을 미치지 않습니다.

따라서 아래 비디오에서 볼 수 있듯이 SDEF가 적용되더라도 섀도우 맵에는 적용되지 않고 대신 LBS로 렌더링된 결과를 보여줍니다.

이 비디오는 MmdStandardMaterial을 사용하여 SDEF가 적용된 MMD 모델을 렌더링한 결과를 보여줍니다. 모델: YYB式初音ミク_10th_v1.02 by SANMUYYB

옵션 3: 셰이더 인젝션을 사용하여 SDEF 적용하기

이 방법은 SDEF를 적용하기 위해 Babylon.js의 셰이더 컴파일 진입점을 수정하며, babylon-mmd에서 제공하는 가장 강력한 SDEF 지원 방법입니다.

이 방법은 옵션 2와 함께 사용하는 것이 가장 좋습니다만, 다른 경우에도 SDEF를 적용할 수 있습니다.

Scene을 생성하기 전에 다음 코드를 실행하여 Engine.createEffect 함수를 수정하세요:

SdefInjector.OverrideEngineCreateEffect(engine);

이것은 프로토타입을 수정하지 않으며, Engine 인스턴스에 createEffect 함수를 추가하는 방식으로 작동합니다.

이 비디오는 셰이더 인젝션을 통해 SDEF가 적용된 MMD 모델을 렌더링한 결과를 보여줍니다. 모델: YYB式初音ミク_10th_v1.02 by SANMUYYB

옵션 4: CPU 바운드 SDEF 스키닝

이 방법은 CPU에서 SDEF 스키닝을 수행하며, 성능은 매우 낮지만 모든 렌더링 파이프라인에서 SDEF를 적용할 수 있습니다.

pluginOptions.mmdmodel.useSdeftrue로 설정하고 모든 메시의 computeBonesUsingShaders 값을 false로 설정하세요.

const assetContainer: AssetContainer = await LoadAssetContainerAsync(
modelFileOrUrl,
scene,
{
pluginOptions: {
mmdmodel: {
useSdef: true
}
}
}
);
assetContainer.addAllToScene();
const modelMesh = assetContainer.meshes[0] as MmdMesh;
for (const mesh of modelMesh.metadata.meshes) mesh.computeBonesUsingShaders = false;

Babylon.js의 Mesh는 CPU 바운드 스키닝을 지원하며, 셰이더로 스키닝을 수행할 수 없는 경우 대체 방법으로 사용됩니다. 그러나 Babylon.js의 CPU 바운드 스키닝은 SDEF를 지원하지 않기 때문에, babylon-mmd는 이를 오버라이드하여 SDEF를 지원합니다.

useSdeftrue로 설정되면, CPU 스키닝 구현이 오버라이드되고, SDEF 지원이 필요한 메시에는 Mesh 대신 SdefMesh가 사용됩니다.

그러나 이 경우 모프 타겟을 동시에 사용할 수 없습니다. 변형을 적용할 때 모프 타겟이 스키닝 전에 적용되어야 하지만, CPU 바운드에서 스키닝이 먼저 처리되고 모프 타겟이 나중에 셰이더에서 적용되어 변형 순서가 잘못됩니다. 따라서 런타임 오류는 발생하지 않지만, 매우 이상한 결과가 발생할 수 있습니다.

SDEF 셰이더 인젝션 구현 세부사항

SDEF를 지원하려면 Babylon.js의 내부 셰이더 코드를 수정하고, Babylon.js 내부의 모든 렌더 파이프라인을 accordingly 수정해야 합니다.

예를 들어, SDEF를 사용하는 MMD 모델로 피사계 심도 효과를 사용하려면, Depth Renderer의 정점 셰이더를 수정한 다음 렌더 파이프라인을 수정하여 SDEF를 적용하는 데 필요한 속성을 추가로 바인딩해야 합니다.

이러한 작업은 사실상 불가능하기 때문에, babylon-mmd는 대신 Babylon.js의 셰이더 컴파일 진입점을 수정하여 SDEF 코드를 삽입하는 방법을 사용합니다.

Babylon.js는 셰이더를 실행하는 데 필요한 속성, 유니폼 등과 함께 셰이더 코드를 Engine.createEffect 함수에 전달합니다. babylon-mmd는 이 프로세스를 가로채어 SDEF 관련 코드를 삽입합니다.

수정된 createEffect 함수는 다음 프로세스를 거칩니다:

  1. 현재 컴파일 중인 셰이더가 스켈레톤을 지원하는지 확인합니다.
  2. 스켈레톤이 지원되는 경우 SDEF용 속성 추가합니다.
  3. SDEF 스키닝 코드 삽입.
  4. 수정된 매개변수를 원래의 createEffect 함수에 전달합니다.

이 접근 방식의 부작용은 SDEF를 지원하지 않는 스켈레톤을 렌더링할 때도 SDEF 코드가 삽입된다는 점입니다.

이 경우, 셰이더 분기 발산으로 인해 LBS를 사용하는 스켈레톤을 렌더링할 때 SDEF를 적용하는 것과 동일한 계산 비용이 발생합니다.