스펙큘러 맵핑(specular map): 언제 구현해 보나

행인:
택스쳐 첫번째스테이지는 디퓨즈맵핑
택스쳐 두번째스테이지는 스펙큘라용 스피어맵핑

기본적으론 두번째패스의 텍스쳐스테이즈옵션에서 컬러부분 오퍼레이션을 ADD 로 하면 됩니다.

만약 스펙큘라가 부분적(마스크맵)으로 먹게 할려면.. 첫번째패스의 디퓨즈맵에 알파부분을 조정합니다..
알파가 1 에 가까울수록 스펙큘라가 많이 먹는걸로 알파가 0 에 가까울수록 스펙큘라는 적게 되는것으로..

이렇게 하시면 일단 첫번째텍스쳐 스테이지의옵션은 디폴트로 놓으시고..
ColorArg1 : Diffuse
ColorArg1 : Texture
ColorOp : Modulate
AlphaArg1 : Diffuse (어짜피 안쓰게 되므로 그냥 끄셔도 됩니다.)
AlphaArg2 : Texture
AlphaOp : Modulate

두번째텍스쳐 스테이지의옵션은..
ColorArg1 : Current
ColorArg2 : Texture
ColorOp : ModulateAlphaAddColor
AlphaArg1 : Current
AlphaArg2 : Texture
AlphaOp : SelectArg2 (스피어맵핑용 소스는 알파가 1이어야겠죠.)

이렇게 하시면 됩니다. 동적으로 스펙큘라의 양을 조절하고 싶으시면..(기본적인 마스크/글로스맵과 함께)
첫번째스테이지의 AlphaArg1 을 TextureFactor 로 조절하시면 될거 같구..
버텍스컬러의 알파를 써야된다면.. 두번째스테이지의 AlphaArg2 를 Diffuse 로 하면 되겠네요..

그래픽카드대부분이 멀티택스쳐를 지원하고.. 스피어맵핑은 텍스쳐매트릭스를 이용해서 텍스쳐좌표를 자동을 뽑을수 있으니 버텍스버퍼를 Lock 하지 않아도 되고요..
대신 디퓨즈맵의 알파는 사용을 못하곘죠..(사람의 머리카락같은거에는 마스크맵을 사용을 못하겠네요. 기본적으로 디퓨즈맵의 알파를 그대로 써야되니..)
그럼~

Zho:

제가 해본 방식은

1) 버텍스 쉐이더에서, 디퓨즈 알파(oD0.a) 에 스페큘러 항을 출력한 다음.

2) 알파 텍스쳐 스테이지에서, 버텍스의 디퓨즈 알파와 텍스쳐의 알파채널을 곱합니다.


코드:
SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
SetTextureStageState(0, D3DTSS_ALPHAOP,     D3DTOP_MODULATE);

그러면, 텍스쳐 알파맵의 이미지 중에서 스페큘러값이 1에 가까운 부분만 추려내집니다.

3) 컬러 텍스쳐 스테이지에서는, 컬러에 알파를 더해주는 텍스쳐 연산을 사용하여 2)에서 계산된
스페큘러맵 만큼의 값을 더해 줍니다.


코드:
SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATECOLOR_ADDALPHA);


그러면, 스페큘러가 적용되는 부분에는 원래의 색상보다 더
밝은 색이 출력됩니다. 물론, 스페큘러항만 그대로 출력되는 것보다는 스페큘러 맵이 곱해지기 때문에
좀 더 부드러운 이미지가 생성되겠죠.

이건, 순전히 제 경험이고, 리니지2가 이렇게 한건지는 저도 잘 모르겠습니다. 스페큘러에 단순한 알파만
들어간 것인지, 아니면 RGB요소가 모두 들어있는 것인지는 제 눈으로는 잘 모르겠더라구요...

손님:

답변감사합니다. 항상 지호님에게 도움받네요..^^;

1번 스테이지에 텍스쳐는 어떻게 해야하나 하다가 아무것도 없길래 0번을 잘못쓰신건가 햇거든요...


코드:
technique AniMesh_vs11_fix
{
    pass p0
    {
        Sampler[0] = <DiffuseMap>;

        TextureFactor = 0xffffffff;

        ColorOp[0]   = Modulate;
        ColorArg1[0] = Texture;
        ColorArg2[0] = Diffuse;

        AlphaOp[0]   = Modulate;
        AlphaArg1[0] = Texture;
        AlphaArg2[0] = Diffuse;

        ColorOp[1]   = ModulateColor_AddAlpha;
        ColorArg1[1] = Current;
        ColorArg2[1] = TFactor;

        VertexShader = compile vs_1_1 vsMain();
        PixelShader  = 0;
    }
}


결국 1번스테이지에서는 그냥 tfactor에 곱했다는..

이렇게 해서 해결됐네요..^^;

감사합니다.

손님:

  VS로 가능은 하네요..

위에서 지호님이 얘기하신 방식으로 VS를 쓰면 가능은 합니다.

코드:
technique PatchSpecular_vs11_fix
{
    pass p0
    {
        // 기본텍스쳐이미지 + 스페큘러맵(알파)
        Sampler[0] = <DiffuseMap>; 
// 라이트맵 + 알파맵 (그냥 용도로 하신다면 rgb흰색의 알파맵만.. ) Sampler[1] = <LightMap>; ColorOp[0] = SelectArg2; ColorArg1[0] = Diffuse; ColorArg2[0] = Texture; AlphaOp[0] = Modulate; AlphaArg1[0] = Diffuse; AlphaArg2[0] = Texture; ColorOp[1] = ModulateColor_AddAlpha; ColorArg1[1] = Current; ColorArg2[1] = Texture; AlphaOp[1] = SelectArg2; AlphaArg1[1] = Current; AlphaArg2[1] = Texture; ColorOp[2] = Disable; AlphaOp[2] = Disable; VertexShader = compile vs_1_1 vsSpecular(); PixelShader = 0; } }

텍스쳐2장의 제한은 곧 VS지원 안되는(에뮬) 카드들의 제한 사항인데 (에뮬은 되겠지만..)

VS로 해서 2장으로 만든다는게 왠지 사용가능성을 생각안하고 목적에만 매달려 만든듯 하네요..-_-;

kaiell:

리니지2 캐릭터 정도의 폴리곤으로는 지호님이 말씀하셨듯이 일반적인 specular연산으로는 부드러운 specular가 나오지 않습니다.

저도 제나름대로 추측해보는 건데요.

저같은 경우는
Irradiance map을 사용하는데요.

큐브맵이나 스피어 맵을 이용한 방식(
irradiance map)이 아닐까 생각됩니다.
이방식은 큐브맵이나 스피어 맵자체를 Specular값자체로 사용하는 방식인데요.

시야벡터와 오브젝트 자체의 노멀벡터를 이용하여 실제 Irradiace맵에 해당되는 위치의 컬러를 찾아서 그 컬러를 현재 재질에 더해주는 방식입니다.

이렇게 적용할려면 2개의 텍스쳐(큐브맵의경우에는 실제로는 7개겠죠^ ^)를 바인딩 하여야 되는데 G-Force2에서는 가능하고요. 그리고 Vertex Shader도 2에서는 사용가능하니깐 이 방식으로 하지 않았을까라고도 생각해볼수는 있겠네요.

Irradiance map을 사용하는 방법은 Real-Time Rendering Second Edition p210을 보시면 알수 있으실겁니다.


그리고 이방식을 사용한 게임은 DoA3와 DoA 비치발리볼이 유명하죠. ^ ^

이 방식의 장점은 로우 폴리곤의 객체라도 부드러운 specular를 먹일수 있다는 장점이 있고요.

단점으로는 동적인 라이팅에는 사용하기 힘들다는 점이 있습니다.
( 그러나 DoA3에서보면 높은곳의 건물에서 싸우다가 떨어져서 네온싸인이 많은 환경쪽으로 가면
Irradiance map을 순간적으로 바꿔 주던데요. 어떻게 보면 꽁수지만 참 적절한 처리라고 생각되어지네요. )


kaiell:

지호님 글을 보고 방금 리니지 2를 플레이 해봤는데 지호님 말씀과 같이 구현한 것이 맞는것 같네요.

그런데
Irradiance맵을 사용하여도 부분적으로 Spcular를 마스크하는 맵을 추가하여 사용하면
마스킹이 가능합니다. 그러나, 이경우에는 Diffuse +
Irradiance + Irradiance Mask 이렇게 3개의 맵이 필요하죠. DoA3의 레이팡의 용그림 옷 같은경우는 그렇게 하였죠.( Diffuse의 알파채널에 Irradiance Mask데이터를 넣는다면 2개의 맵으로도 가능하겠네요 )

효율성을 생각하였을때는 지호님이 말씀하신 방법이 최고인 것 같습니다.
( 결론적으로는 지호님같은 방식은 Specular계산을 직접해줘서 Specular마스크 데이터와 같이 연산하고
제가 말한 방식은 Specular계산을
Irradiance를 이용하여 구해서 Specular마스크 테이터와 같이 연산하는 것의 차이인것 같네요. )

참조:

http://www.gpgstudy.com/forum/viewtopic.php?t=2318&postdays=0&postorder=asc&highlight=Irradiance&start=0