최근, 게임회사에 들어가기 위해 여러 작업을 하고 있습니다만...
제가 해온건 거의 Network 관련이라 (이쪽이 은근히 재미 있습니다..) Network 관련 프로그램을 짜거나 했었는데..

막상 이력서 쓸려고 보니, 게임회사에서 한눈에 볼 수 있는 작품이 없더군요...
(Network 경우 DB 셋팅이니, 서버, 클라이언트 소스 동시에 검토 하니... 의외로 잘 안먹히는 느낌입니다..)

역시 시대는 3D... 3D로 최소한의 능력을 보여주자 해서 (라기 보단 뭔가 네트워크랑 2D(MFC)로 작품내니 무시당하는 느낌이....)이전에 만들었던 맵툴 소스를 열어서 디버깅 했습니다만...

그땐, 셀러논 시스템이었고, 지금은 AMD 시스템... 윈도우도 비스타 -> 7...
그리고 CG 컴파일러 문제....  여튼 지금 보니 프로그램이 엄청 꼬여있더군요...

그래서 기분을 가볍게 하고, 처음부터 갈아 엎어서 만들고 있는 도중....

맵툴의 맵 시스템은, 친구에게 받아서 대략 구조 검토하고 개조한 뒤, 위에 좀 괜찮은 효과를 씌울까 하다가
역시 믿을 만한건 화면 효과라.... 이전의 수면효과를 CG (NVidia 의)를 해볼려 하다가..
기초 부터 하는게 좋을꺼 같아 HLSL 로 하기로 했습니다.

그런데.. 이거 의외로 소스가 없더군요.(특히 우리나라..)
이거 받은 친구에게 물어봤는데.. 수면 쉐이더가 그렇게 고급기술은 아니라고 들었습니다만...

어떻게 구현하는지 소스 좀 구경하려고 하니 죄다 스샷 찍어놓고, "나 이거 했어요" 느낌이더군요.....

음.......
결국, 이전에 오우거 엔진 공부할때 네이버 오우거 카페의 주바리님의 바다 쉐이더 분석을 가지고 공부 한적이 있어서, 그걸 이용하기로 했습니다.
하지만, 오우거 엔진의 경우 CG를 사용하기에 HLSL코드가 아니라 살짝 요령을 가지고 바꿔 봤습니다.

제 글은 그냥 HLSL소스를 필요로 하는.... (제가 봤을때 이건 대학생 레벨 정도 일꺼 같은..)
분들꼐 많은 도움이 되시길 바라면서...
제 글에 이해가 잘 안가시는 분은
http://starlike.cafe24.com/moniwiki/wiki.php/OgreOceanDemoAnalysis?action=show
이쪽의 주바리님이 분석한걸 봐주시기 바랍니다.


처음 소스 입니다.
파란 화면 평면을 띄어 봅시다..

float4x4 WorldViewProj;
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
struct v2f {
    float4 Position  : POSITION;  // in clip space
};
v2f main_vs( a2v IN)
{
    v2f OUT;
    float4 P = IN.Position;
   
    OUT.Position = mul(P, WorldViewProj);
   
    return OUT;
}
/******************************************************************************/
float4 main_fs(v2f IN) : COLOR
{
    return float4(0, 0, 1, 1);
}


이건 삼각형 2개로 이루워진... 말 그래도 그냥 평면입니다.


파랗게 변했군요..
픽셀 출력이 float(0,0,1,1) 이니까 어쩔 수 없겠지만요 ...
주바리님 소스에 의하면 버텍스 쉐이더의 mul 부분의 연산 순서가 바껴있는데요..
이건 CG와 HLSL의 컴파일 차이인지... 음...

다음 차례 입니다.

float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float4 eyePosition;
float time;
float2 textureScale = float2(25, 25);
texture texture0;
texture texture1;
 
sampler2D texSampler : TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
     
    float2 bumpCoord0 : TEXCOORD3;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
void main_vs(in a2v IN, out v2f OUT)
{
    float4 P = IN.Position;
   
    OUT.Position = mul(P, WorldViewProj);
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale;
}
/******************************************************************************/
uniform sampler2D NormalMap;
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
 
    float3 N = normalize(t0.xyz);
    float3x3 m; // tangent to world matrix
    m[0] = float3(1, 0, 0);
    m[1] = float3(0, 0, 1);
    m[2] = float3(0, 1, 0);
   
    N = normalize( mul( N, m ) );
   
    float amplitude = saturate(dot(N, float3(0, 1, 0)));
   
    OUT.color = float4(0, 0, amplitude, 1);
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}


텍스쳐를 씌었습니다.... 멀리 떨어져서 그냥 다 검은색으로 보이네요.

평면에 웨이브를 줘 보겠습니다.
float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float4 eyePosition;
float time;
float2 textureScale = float2(25, 26);
float waveFreq = 0.028;
float waveAmp = 1.8;

texture texture0;
texture texture1;
 
/******************************************************************************/
sampler2D texSampler : TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};
/******************************************************************************/
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
     
    float2 bumpCoord0 : TEXCOORD3;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
/******************************************************************************/
void main_vs(in a2v IN, out v2f OUT)
{
    #define NWAVES 2
    Wave wave[NWAVES] = {
        { 1.0, 1.0, 0.5, float2(-1, 0) },
        { 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
    };
    wave[0].freq = waveFreq;
    wave[0].amp = waveAmp;
    wave[1].freq = waveFreq * 3.0;
    wave[1].amp = waveAmp * 0.33;
    float4 P = IN.Position;
   
    int i = 0;
    float angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
    P.y += wave[i].amp * sin( angle );
    OUT.Position = mul(P, WorldViewProj);
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale;
}
/******************************************************************************/
uniform sampler2D NormalMap;
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
 
    float3 N = normalize(t0.xyz);
    float3x3 m; // tangent to world matrix
    m[0] = float3(1, 0, 0);
    m[1] = float3(0, 0, 1);
    m[2] = float3(0, 1, 0);
   
    N = normalize( mul( N, m ) );
   
    float amplitude = saturate(dot(N, float3(0, 1, 0)));
   
    OUT.color = float4(0, 0, amplitude, 1);
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}




음.. 이렇게 확대해 봐도 잘 모르겠습니다..
뭔가 미묘하게 움직이는게 보이기는 하는데요
너무 어두워서... >_<


위의 소스를 좀더 개량해 봅니다.
float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float4 eyePosition;
float time;
float2 textureScale = float2(25, 26);
float waveFreq = 0.028;
float waveAmp = 1.8;
texture texture0;
texture texture1;
 
/******************************************************************************/
sampler2D texSampler : TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};
/******************************************************************************/
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
     
    float2 bumpCoord0 : TEXCOORD3;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
/******************************************************************************/
void main_vs(in a2v IN, out v2f OUT)
{
    #define NWAVES 2
    Wave wave[NWAVES] = {
        { 1.0, 1.0, 0.5, float2(-1, 0) },
        { 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
    };
    wave[0].freq = waveFreq;
    wave[0].amp = waveAmp;
    wave[1].freq = waveFreq * 3.0;
    wave[1].amp = waveAmp * 0.33;
    float4 P = IN.Position;
   
    float angle;
    for(int i = 0; i<NWAVES; ++i)
    {
        angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
        P.y += wave[i].amp * sin( angle );
    }
    OUT.Position = mul(P, WorldViewProj);
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale;
}
/******************************************************************************/
uniform sampler2D NormalMap;
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
 
    float3 N = normalize(t0.xyz);
    float3x3 m; // tangent to world matrix
    m[0] = float3(1, 0, 0);
    m[1] = float3(0, 0, 1);
    m[2] = float3(0, 1, 0);
   
    N = normalize( mul( N, m ) );
   
    float amplitude = saturate(dot(N, float3(0, 1, 0)));
   
    OUT.color = float4(0, 0, amplitude, 1);
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}


음... 뭔가 더 미묘하게 움직이는 느낌이 드는군요.. ㅋㅋ

float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float4 eyePosition;
float time;
float2 textureScale = float2(25, 26);
float waveFreq = 0.028;
float waveAmp = 1.8;
float BumpScale = 1;
texture texture0;
texture texture1;
 
/******************************************************************************/
sampler2D texSampler : TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};
/******************************************************************************/
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
   
// tangent to obj space transform
    float3 rotMatrix1 : TEXCOORD0; // first row of the 3x3 transform
    float3 rotMatrix2 : TEXCOORD1; // second row of the 3x3 transform
    float3 rotMatrix3 : TEXCOORD2; // third row of the 3x3 transform
    float2 bumpCoord0 : TEXCOORD3;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
/******************************************************************************/
void main_vs(in a2v IN, out v2f OUT)
{
    #define NWAVES 2
    Wave wave[NWAVES] = {
        { 1.0, 1.0, 0.5, float2(-1, 0) },
        { 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
    };
    wave[0].freq = waveFreq;
    wave[0].amp = waveAmp;
    wave[1].freq = waveFreq * 3.0;
    wave[1].amp = waveAmp * 0.33;
    float4 P = IN.Position;
   
     // sum waves
    float ddx = 0.0, ddy = 0.0;
    float deriv;
    float angle;
   
    for(int i = 0; i<NWAVES; ++i)
    {
        angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
        P.y += wave[i].amp * sin( angle );
        deriv = wave[i].freq * wave[i].amp * cos(angle);
        ddx -= deriv * wave[i].dir.x;
        ddy -= deriv * wave[i].dir.y;
    }
   
    // compute the 3x3 tranform from tangent space to object space
    // first rows are the tangent and binormal scaled by the bump scale
    OUT.rotMatrix1.xyz = BumpScale * normalize(float3(1, ddy, 0)); // Binormal
    OUT.rotMatrix2.xyz = BumpScale * normalize(float3(0, ddx, 1)); // Tangent
    OUT.rotMatrix3.xyz = normalize(float3(ddx, 1, ddy)); // Normal
    OUT.Position = mul(P, WorldViewProj);
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale;
}
/******************************************************************************/
uniform sampler2D NormalMap;
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
 
    float3 N = normalize(t0.xyz);
    float3x3 m; // tangent to world matrix
    m[0] = float3(1, 0, 0);
    m[1] = float3(0, 0, 1);
    m[2] = float3(0, 1, 0);
   
    N = normalize( mul( N, m ) );
   
    float amplitude = saturate(dot(N, float3(0, 1, 0)));
   
    OUT.color = float4(0, 0, amplitude, 1);
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}


노멀 맵핑을 교정한 소스의 결과 입니다.....
이렇게 봐도... 음.... 역시 그냥 검은 평면에 뭔가 파란 점이 떠다니는 정도로 밖에 안보이네요..


여기에 주변광을 넣을 준비 하는 소스입니다.
float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float time;
float2 textureScale = float2(25, 26);
float waveFreq = 0.028;
float waveAmp = 1.8;
float BumpScale = 1;
float4 eyePosition;
float4 deepColor = float4(0, 0.3, 0.5, 1.0);
float4 shallowColor = float4(0, 1, 1, 1.0);
float waterAmount = 0.3;
texture texture0;
texture texture1;
 
/******************************************************************************/
sampler2D NormalMap: TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};
/******************************************************************************/
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
   
// tangent to obj space transform
    float3 rotMatrix1 : TEXCOORD0; // first row of the 3x3 transform
    float3 rotMatrix2 : TEXCOORD1; // second row of the 3x3 transform
    float3 rotMatrix3 : TEXCOORD2; // third row of the 3x3 transform
    float2 bumpCoord0 : TEXCOORD3;
    float2 bumpCoord1 : TEXCOORD4;
    float2 bumpCoord2 : TEXCOORD5;
 
   float3 eyeVector : TEXCOORD7;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
/******************************************************************************/
void main_vs(in a2v IN, out v2f OUT)
{
    #define NWAVES 2
    Wave wave[NWAVES] = {
        { 1.0, 1.0, 0.5, float2(-1, 0) },
        { 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
    };
    wave[0].freq = waveFreq;
    wave[0].amp = waveAmp;
    wave[1].freq = waveFreq * 3.0;
    wave[1].amp = waveAmp * 0.33;
    float4 P = IN.Position;
   
     // sum waves
    float ddx = 0.0, ddy = 0.0;
    float deriv;
    float angle;
   
    for(int i = 0; i<NWAVES; ++i)
    {
        angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
        P.y += wave[i].amp * sin( angle );
        deriv = wave[i].freq * wave[i].amp * cos(angle);
        ddx -= deriv * wave[i].dir.x;
        ddy -= deriv * wave[i].dir.y;
    }
   
    // compute the 3x3 tranform from tangent space to object space
    // first rows are the tangent and binormal scaled by the bump scale
    OUT.rotMatrix1.xyz = BumpScale * normalize(float3(1, ddy, 0)); // Binormal
    OUT.rotMatrix2.xyz = BumpScale * normalize(float3(0, ddx, 1)); // Tangent
    OUT.rotMatrix3.xyz = normalize(float3(ddx, 1, ddy)); // Normal
    OUT.Position = mul(P, WorldViewProj);
   
    // calculate texture coordinates for normal map lookup
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale;
    OUT.bumpCoord1.xy = IN.TexCoord * textureScale * 2.0;
    OUT.bumpCoord2.xy = IN.TexCoord * textureScale * 4.0;
 
    OUT.eyeVector = P.xyz - eyePosition; // eye position in vertex space
}
/******************************************************************************/
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
    float4 t1 = tex2D(NormalMap, IN.bumpCoord1) * 2.0 - 1.0;
    float4 t2 = tex2D(NormalMap, IN.bumpCoord2) * 2.0 - 1.0;
    float3 N = t0.xyz + t1.xyz + t2.xyz;
   
    float3x3 m; // tangent to world matrix
    m[0] = IN.rotMatrix1;
    m[1] = IN.rotMatrix2;
    m[2] = IN.rotMatrix3;
   
    N = normalize( mul( m, N ) );
   
    float3 E = normalize(IN.eyeVector);
   
    float facing = 1.0 - max(dot(-E, N), 0);
    float4 waterColor = lerp(shallowColor, deepColor, facing) * waterAmount;
   
    OUT.color = waterColor;
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}


음... 역시 아직도 검은 평면.. orz
포기하지 않고 소스를 좀더 수정해 봅니다.

float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float time;
float2 textureScale = float2(25, 26);
float waveFreq = 0.028;
float waveAmp = 1.8;
float BumpScale = 1;
float2 bumpSpeed = float2(0.015, 0.005);
float4 eyePosition;
float4 deepColor = float4(0, 0.3, 0.5, 1.0);
float4 shallowColor = float4(0, 1, 1, 1.0);
float waterAmount = 0.3;
texture texture0;
texture texture1;
 
/******************************************************************************/
sampler2D NormalMap: TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};
/******************************************************************************/
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
   
// tangent to obj space transform
    float3 rotMatrix1 : TEXCOORD0; // first row of the 3x3 transform
    float3 rotMatrix2 : TEXCOORD1; // second row of the 3x3 transform
    float3 rotMatrix3 : TEXCOORD2; // third row of the 3x3 transform
    float2 bumpCoord0 : TEXCOORD3;
    float2 bumpCoord1 : TEXCOORD4;
    float2 bumpCoord2 : TEXCOORD5;
    float3 eyeVector : TEXCOORD7;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
/******************************************************************************/
void main_vs(in a2v IN, out v2f OUT)
{
    #define NWAVES 2
    Wave wave[NWAVES] = {
        { 1.0, 1.0, 0.5, float2(-1, 0) },
        { 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
    };
    wave[0].freq = waveFreq;
    wave[0].amp = waveAmp;
    wave[1].freq = waveFreq * 3.0;
    wave[1].amp = waveAmp * 0.33;
    float4 P = IN.Position;
   
     // sum waves
    float ddx = 0.0, ddy = 0.0;
    float deriv;
    float angle;
   
    for(int i = 0; i<NWAVES; ++i)
    {
        angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
        P.y += wave[i].amp * sin( angle );
        deriv = wave[i].freq * wave[i].amp * cos(angle);
        ddx -= deriv * wave[i].dir.x;
        ddy -= deriv * wave[i].dir.y;
    }
   
    // compute the 3x3 tranform from tangent space to object space
    // first rows are the tangent and binormal scaled by the bump scale
    OUT.rotMatrix1.xyz = BumpScale * normalize(float3(1, ddy, 0)); // Binormal
    OUT.rotMatrix2.xyz = BumpScale * normalize(float3(0, ddx, 1)); // Tangent
    OUT.rotMatrix3.xyz = normalize(float3(ddx, 1, ddy)); // Normal
    OUT.Position = mul(P, WorldViewProj);
   
    // calculate texture coordinates for normal map lookup
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale + time * bumpSpeed;
    OUT.bumpCoord1.xy = IN.TexCoord * textureScale * 2.0 + time * bumpSpeed * 4.0;
    OUT.bumpCoord2.xy = IN.TexCoord * textureScale * 4.0 + time * bumpSpeed * 8.0;
 
    OUT.eyeVector = P.xyz - eyePosition; // eye position in vertex space
}
/******************************************************************************/
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
    float4 t1 = tex2D(NormalMap, IN.bumpCoord1) * 2.0 - 1.0;
    float4 t2 = tex2D(NormalMap, IN.bumpCoord2) * 2.0 - 1.0;
    float3 N = t0.xyz + t1.xyz + t2.xyz;
   
    float3x3 m; // tangent to world matrix
    m[0] = IN.rotMatrix1;
    m[1] = IN.rotMatrix2;
    m[2] = IN.rotMatrix3;
   
    N = normalize( mul( N, m ) );
   
    float3 E = normalize(IN.eyeVector);
   
    float facing = 1.0 - max(dot(-E, N), 0);
    float4 waterColor = lerp(shallowColor, deepColor, facing) * waterAmount;
   
    OUT.color = waterColor;
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}




여기까지 하니까 이제 뭔가 보이기 시작하네요.
평면이 꿈툴거리는것도 뭔가 그럴듯 합니다.

이제 여기에 SkyCube, 하늘 Texture 를 씌여 줍니다.
오우거 소스의 이 소스는 원래 기반이 SkyCube 이므로 SkyCube 로 텍스쳐를 불러오는게
좀더 자연스러운 수면효과를 만들 수 있습니다.

float4x4 WorldViewProj : WORLDVIEWPROJ; //our world view projection matrix
float time;
float2 textureScale = float2(25, 26);
float waveFreq = 0.028;
float waveAmp = 1.8;
float BumpScale = 0.2;
float2 bumpSpeed = float2(0.015, 0.005);
float4 eyePosition;
float4 deepColor = float4(0, 0.3, 0.5, 1.0);
float4 shallowColor = float4(0, 1, 1, 1.0);
float waterAmount = 0.3;
float4 reflectionColor = float4(0.95, 1, 1, 1.0);
float reflectionAmount = 1.0;
float reflectionBlur = 0.0;
    
float fresnelPower = 5.0;
float fresnelBias = 0.328;
float hdrMultiplier = 0.471;

texture texture0;
texture texture1;
 
/******************************************************************************/
sampler2D NormalMap: TEXUNIT0 = sampler_state
{
 Texture   = (texture0);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
sampler2D EnvironmentMap: TEXUNIT0 = sampler_state
{
 Texture   = (texture1);
    MIPFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
};
struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};
/******************************************************************************/
//application to vertex structure
struct a2v {
    float4 Position : POSITION;   // in object space
    float2 TexCoord : TEXCOORD0;
};
//vertex to pixel shader structure
struct v2f {
    float4 Position  : POSITION;  // in clip space
   
// tangent to obj space transform
    float3 rotMatrix1 : TEXCOORD0; // first row of the 3x3 transform
    float3 rotMatrix2 : TEXCOORD1; // second row of the 3x3 transform
    float3 rotMatrix3 : TEXCOORD2; // third row of the 3x3 transform
    float2 bumpCoord0 : TEXCOORD3;
    float2 bumpCoord1 : TEXCOORD4;
    float2 bumpCoord2 : TEXCOORD5;
    float3 eyeVector : TEXCOORD7;
};
//pixel shader to screen
struct p2f
{
    float4 color    : COLOR0;
};
/******************************************************************************/
void main_vs(in a2v IN, out v2f OUT)
{
    #define NWAVES 2
    Wave wave[NWAVES] = {
        { 1.0, 1.0, 0.5, float2(-1, 0) },
        { 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
    };
    wave[0].freq = waveFreq;
    wave[0].amp = waveAmp;
    wave[1].freq = waveFreq * 3.0;
    wave[1].amp = waveAmp * 0.33;
    float4 P = IN.Position;
   
     // sum waves
    float ddx = 0.0, ddy = 0.0;
    float deriv;
    float angle;
   
    for(int i = 0; i<NWAVES; ++i)
    {
        angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
        P.y += wave[i].amp * sin( angle );
        deriv = wave[i].freq * wave[i].amp * cos(angle);
        ddx -= deriv * wave[i].dir.x;
        ddy -= deriv * wave[i].dir.y;
    }
   
    // compute the 3x3 tranform from tangent space to object space
    // first rows are the tangent and binormal scaled by the bump scale
    OUT.rotMatrix1.xyz = BumpScale * normalize(float3(1, ddy, 0)); // Binormal
    OUT.rotMatrix2.xyz = BumpScale * normalize(float3(0, ddx, 1)); // Tangent
    OUT.rotMatrix3.xyz = normalize(float3(ddx, 1, ddy)); // Normal
    OUT.Position = mul(P, WorldViewProj);
   
    // calculate texture coordinates for normal map lookup
    OUT.bumpCoord0.xy = IN.TexCoord * textureScale + time * bumpSpeed;
    OUT.bumpCoord1.xy = IN.TexCoord * textureScale * 2.0 + time * bumpSpeed * 4.0;
    OUT.bumpCoord2.xy = IN.TexCoord * textureScale * 4.0 + time * bumpSpeed * 8.0;
 
    OUT.eyeVector = P.xyz - eyePosition; // eye position in vertex space
}
/******************************************************************************/
void main_fs(in v2f IN, out p2f OUT)
{
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
    float4 t1 = tex2D(NormalMap, IN.bumpCoord1) * 2.0 - 1.0;
    float4 t2 = tex2D(NormalMap, IN.bumpCoord2) * 2.0 - 1.0;
    float3 N = t0.xyz + t1.xyz + t2.xyz;
   
    float3x3 m; // tangent to world matrix
    m[0] = IN.rotMatrix1;
    m[1] = IN.rotMatrix2;
    m[2] = IN.rotMatrix3;
   
    N = normalize( mul( N, m ) );
   
    // reflection
    float3 E = normalize(IN.eyeVector);
    float4 R;
    R.xyz = reflect(E, N);
  
    // Ogre conversion for cube map lookup
    R.z = -R.z;
    R.w = reflectionBlur;
    float4 reflection = tex2Dbias(EnvironmentMap, R);
    // cheap hdr effect
    reflection.rgb *= (reflection.r + reflection.g + reflection.b) * hdrMultiplier;
    // fresnel
    float facing = 1.0 - max(dot(-E, N), 0);
    float fresnel = saturate(fresnelBias + pow(facing, fresnelPower));
    float4 waterColor = lerp(shallowColor, deepColor, facing) * waterAmount;
    reflection = lerp(waterColor,  reflection * reflectionColor, fresnel) * reflectionAmount;
 
    OUT.color = waterColor + reflection;
}
technique water
{
    pass p0
    {
        vertexshader = compile vs_1_1 main_vs();
        pixelshader = compile ps_2_0 main_fs();
    }
}



음.... 꽤 그럴듯한 효과가 나왔습니다.

이것만으론 뭐가 뭔지 역시 모르겠습니다 하실 분도 계실꺼 같아서
소스도 같이 올립니다.







물.. 일단 오우거 그걸로 그렸음..

문제는 모달리스상자가 안뜨는데.. 이거 좀 조사해 보고

안개... 레이어 안개 넣고 하면 금방 끝낼꺼 같은......

된것

1. 바다 표현
2. 안개, exp, 선형
3. 카메라, 인터페이스 구현된거 전부 조절 가능하게
4. 아이콘 바꿈..


앞으로 할껏..
DS 맵툴 보고 이것저것 추가
픽킹
모달리스 상자 버그 개선...
레이어 안개 추가
비 추가 (쉐이더)
눈 추가 (파티클?)


작업은.. 4월부터 10일간...

원문 : http://www.gamasutra.com/features/20060103/kryachko_01.shtml

 

Book Excerpt:


GPU Gems 2
:


Using Vertex Texture Displacement for

 

Realistic Water Rendering

 

 

다음은 Addison-Wesley Professional 에서 발간한 GPU Gems 2: Programming Techniques for High-Performance Graphics and General-Purpose Computation (ISBN 0321335597) 의 18장에서 발췌한 것이다.

 

--

 

물 표면은 컴퓨터 그래픽에서 일반적이며, 특히 게임에서 많이 사용된다. 그것들은 씬의 사실성의 수준을 매우 증가시켜줄 수 있는 핵심적인 요소들이다. 그러나 그것들을 묘사하는 것은 현실적으로 어려운 문제이다. 왜냐하면 가시적인 복잡함이 물 표면의 움직임에서 표현될 뿐만 아니라 물과 상호작용하는 조명도 표현되기 때문이다. 이 장은 Pacific Fighter 라는 게임을 위해서 현실감 있는 대양을 표현하기 위해서 개발되었던 기법에 대해서 설명한다.

 

현대의 그래픽 하드웨어는 물 표면 렌더링을 위해서 사용될 수 있는 DirectX Shader 3.0 모델과 함께 유용한 많은 기능들을 제공한다. 이 장은 이러한 기능들 중에서 정점 텍스처링을 사용하여 렌더링된 물 표면의 사실감을 증가시키는 방법에 대해서 논의할 것이다. Figure18-1 은 샘플을 보여준다. 부가적으로 우리는 정점 프로그램의 성능을 증가시키기 위해 다른 기법(branching) 도 사용할 것이다.

 

18.1 Water Models

 

물 애니메이션과 렌더링을 위해서 많은 기법들이 개발되었다. 가장 주목할 만한 그리고 사실감 있는 외형을 보이는 것은 fluid dynamics 와 (Tessendorf 2001 과 같은) Fast Fourier Transforms(FFTs)이다. 이들 기법은 매우 사실적인 결과를 보여 주지만 불운하게도 그것들은 상당한 양의 계산을 필요로 하기 때문에 대화식 응용프로그램에는 적합하지 않다.



 
Figure 18-1. The Benefit of Displacement Mapping
Water surface rendered (left) with displacement mapping and (right) without displacement mapping.
 

At the other extreme, 대부분의 게임들은 현재 매우 간단한 물 모델을 사용하며, 대부분은 가시적 디테일을 생성하기 위해서 노멀 맵을 채용하고 있다. 불운하게도 이러한 접근은 충분한 사실감을 살리지 못하며 표면의 웨이브를 충실히 재생하지 않는다.

 

우리는 단순한 노말 맵 물 렌더링 기법의 속도와 FFT 와 같은 접근의 가시적 품질을 결합하는 기법을 찾았다.

 

18.2 Implementation

 

우리의 구현은 조명 계산을 위해서 노멀맵을 채용하는 알고리즘에 기반한다. 노멀맵은 고 주파수 웨이브에서 좋은 디테일을 충실히 재생하기 때문에, 우리는 조명 계산을 위해 그것들을 사용한다. 그러나 부가적으로 우리는 큰 크기의 저주파수 웨이브를 사용해 물 메시를 기하학적으로 교란시켰다.

 

18.2.1 Water Surface Model

 

우리의 물 표면 모델은 몇 개의 높이 맵의 최상위 위치(superposition)에 기반하는데, 이 높이맵은 공간과 시간 단위로 타일화되어 있다. 각 텍스처는 그 스펙트럼에서의 하나의 "harmonic" 이나 "octave"를 표현하며, 그 텍스처들은 푸리에 합성(Fourier synthesis)으로서 서로 추가된다. 이 텍스처들은 높이 맵이라고 불리운다. 왜냐하면 각 값은 수평면 위의 해당 지점의 값을 표현하기 때문이다.

 

높이맵은 아티스트에 있어서 편리한 것이다 : 그것들을 생성하는 것은 그레이스케일 이미지를 그리는 것만큼 단순하다. Figure 18-2 를 참조하라. 높이맵을 사용해 아티스트들은 쉽게 물 애니메이션이 개별 웨이브를 아래로 내리는 파라미터를 제어할 수 있다. 그들의 모양을 그리기만 하면 된다. 높이맵은 정점텍스처와도 잘 작동한다 : 정점 위치를 수직적으로 바꾸기 위해서 그것들을 사용하는 것은 간단하다.

 


 
Figure 18-2. A Height Map used for Water Displacement

 

 
Figure 18-2. Water Displacement 에 사용된 높이맵
 

서로 다른 공간적 시간적 규모를 가진 몇 개의 높이맵을 결합함으로써, 우리는 가시적으로 복잡한 애니메이션을 만들 수 있다 :


계수 A 와 B, 그리고 sum 아래의 항(term)의 숫자는 가장 미적으로 만족할만한 결과를 만들기 위해서 반복되는 패턴 인공물(artifact)를 제거하는 동안 학습적으로 선택된다. Pacific Fighters 에서 우리는 네 개의 높이 맵을 조명 계산을 위해 합쳤으며, 그 중 가장 큰 규모를 가진 두 개는 displacement 매핑을 위해서 사용되었다. 이것은 10 cm 부터 40 km 까지의 규모를 가진 대양을 움직이는 시뮬레이션을 위해 충분했다.

 

18.2.2 Implementation Details
 

우리가 수행할 필요가 있는 모든 계산들은 두 개의 그룹으로 분류될 수 있다 : 기하학적 displacement 계산 및 조명 계산이다. 우리의 물 표면은 잘 세분화(tessellated) 되었기 때문에, 단위 프로그램 수준에서 조명 계산을 수행하고 정점 스테이지에 대한 displacement 매핑을 언로드하는 것이 합리적이다.(원문 : Because our water surface is finely tessellated, it is reasonable to perform lighting calculations at the fragment program level, offloading the displacement mapping to the vertex stage.) 또한 조명 계산을 정점 스테이지에서 수행하는 것은 특히 먼 거리에서 가시적 인공물을 생성할 수 있다.

 

이 글을 작성하는 시점에는 정점 텍스처링을 수행하는 기능을 가진 하드웨어가 Geforce 6 Series GPU 와 최신의 NVIDIA Quadro FX GPU 밖에 없었다. 이 하드웨어 상에서 정점 텍스처를 구현하는 것은 약간의 제약을 가지고 있다; 특히 정점 텍스처는 반드시 요소당 32 비트인 텍스처여야만 하며, 부동 소수점 수여야만 한다. 그리고 그것들은 가장 가까운 곳의 필터링을 제외하고는 어떠한 필터링 모드도 사용할 수 없다. 그럼에도 불구하고 그것들은 이 장에서 기술된 기법을 위해서 매우 유용함이 입증되었다.

 

18.2.3 Sampling Height Maps
 

우리의 구현은 정점 당 높이맵을 샘플링하고, 정점 프로그램에서 결과 displacement 값을 계산한다. 샘플링을 위해서 우리는 원형 그리드(radial grid)를 사용하고 카메라 위치의 중심에 배치했다. 이 그리드는 Figure 18-3 에 보이는 것처럼 관찰자에게 가까우면 더욱 디테일하게 보이도록 하기 위한 방식으로 세분화된다(tessellated).

 

다음 공식은 원형 그리드를 위한 정점 위치가 계산되는 방식을 보여 준다.

 




 
Figure 18-3. Radial Grid for Sampling Vertex Textures
 
 

이러한 접근을 통해 우리는 자연적으로 거리 기반 세분화를 획득했으며, 이것은 간단한 LOD 를 제공했다. ROAM 이나 SOAR 지형 렌더링 알고리즘과 같은 다른 접근이 여기서 사용될 수있지만, 그것들은 CPU 측에서 매우 많은 양의 작업을 요구하며, 결국 정점 텍스처를 사용하는 모든 이점을 제거할 것이다. 적합한 세분화를 하기 위해 CPU 상에서 높이맵을 렌더링하는 다른 기법에 대해 알고자 한다면 이 책의 2장 "Terrain Rendering Using GPU-Based Geometry Clipmaps,"을 참조하라.

 

Listing 18-1 은 단일 높이 맵과 원형 그리드로부터의 샘플링을 구현하는 간단한 정점 쉐이더를 보여 준다.

 

float4 main( float4 position : POSITION,
  uniform sampler2D tex0,
  uniform float4x4 ModelViewProj,
  uniform float4 DMParameters, // displacement map parameters
  uniform float4
VOfs) : POSITION
{
  // Read vertex packed as (cos(), sin(), j)
   float4
INP = position;

  // Transform to radial grid vertex
  
INP.xy = INP.xy * ( pow (INP.z, 4) * VOfs.z);

  // Find displacement map texture coordinates
  // VOfs.xy, DMParameters.x - Height texture offset and scale
  float2
t = (INP.xy + VOfs.xy) * DMParameters.x;

  // Fetch displacement value from texture (lod 0)
  float
vDisp = tex2D (tex0, t).x;

  // Scale fetched value from 0..1:
  // DMParameters.y - water level
  // DMParameters.z - wavy amplitude
  
INP.z = DMParameters.y + (vDisp - 0.5) * DMParameters.z;

  // Displace current position with water height
  // and project it
  
return mul (ModelViewProj, INP);
}

Listing 18-1. Vertex Shader for Sampling from a Height Map Using the Radial Grid Geometry

 

 

18.2.4 Quality Improvements and Optimizations

 

이선형(bilenear) 필터링을 위해 높이 포장(packing) 정점 텍스처 fetch(가지고 오는 것)는 매우 비용이 많이 들 수 있다. GeForce 6 시리즈 하드웨어 상에서 단일 정점 텍스처 fetch는 정점 프로그램을 매우 느리게 만들 수 있다. 그래서 우리는 텍스처 fetch 의 개수를 정점 프로그램 내부에서 최소화하기를 원했다. 다시 말해 텍스처 값 상에서 어떤 종류의 필터링을 수행하기를 원했다; 그렇지 않으면 가시 품질이 매우 감소될 것이다. 전통적으로 잘 알려진 필터링 기법은 이선형(bilinear) 필터링과 삼선형(trilinear) 필터링이다. 이선형 필터링은 텍스처 fetch 의 좌표에 인접한 네 텍셀의 가중치화된 평균을 계산한다. 삼선형 필터링은 인접한 밉 레벨에서 이선형 색인의 결과를 평균내는데, 각각은 관련 LOD 분수(fraction)에 의해서 가중치가 매겨 진다. 현재 세대의 그래픽 하드웨어는 정점 텍스처 값에 대한 어떠한 형식의 필터링도 지원하지 않기 때문에, 우리는 명시적인 수학 명령어를 사용해 쉐이더에서 필터링을 에뮬레이트해야 한다. 무경험적으로 구현했을 경우 가장 간단한 이선형 필터라도 단일 필터링된 값을 계산하기 위해서 4 개의 텍스처 색인(lookup)을 요구할 것이다. 삼선형 필터는 두 배의 텍스처 색인을 요구할 것이다. 텍스처 fetch 를 감소시키기 위해서는 필터링이 필수적이기 때문에, 우리는 특별한 방법으로 우리의 텍스처를 만들어서 각 텍셀이 단일 이선형 텍스처 색인에 대해서 필요한 데이터를 모두 포함하도록 한다. 이것은 우리 높이맵이 기본적으로 하나의 요소를 가진 텍스처이기 대문에 가능하며, 우리는 4개의 높이 값을 네개의 요소를 가진 텍스처의 단일 텍셀에 포장할 수 있다 :


 

 

여기에서 i = 0... N -1 이고, j = 0... M-1 이다. H 는 높이맵 값이며, F 는 필터링 함수이다. 그리고 A 는 포장된 출력 텍스처이다.

 

Listing 18-2 는 위에 보여 준 것처럼 포장하여 이선형 텍스처 색인을 정점 텍스처로 구현한다.

 


float tex2D_bilinear4x( uniform sampler2D tex,
  float4 t,
  float2 Scales)

{
  float size = Scales.x;
  float scale = Scales.y;

  float4 tAB0 = tex2Dbias (tex, t);

  float2 f = frac (t.xy * size);
  float2 tAB = lerp (tAB0.xz, tAB0.yw, f.x);
  return lerp (tAB.x, tAB.y, f.y);
}

Listing 18-2. Efficient Bilinear Texture Interpolation in Vertex Shaders
Based on fetching the appropriate four height components with a single texture fetch.

 

우리는 삼선형 필터링을 위해 이 접근을 확장할 수 있다. 삼선형 필터링은 분수형(fractional) LOD 값을 요구하기 때문에 우리는 카메라로부터의 거리를 LOD 의 좋은 근사치로 사용할 수 있다. Listing 18-3 의 코드는 삼선형 텍스처 색인을 포장된 정점 텍스처에 구현한다.

 


float tex2D_trilinear( uniform sampler2D tex,
  float4 t,
  float2 Scales)
{
  float fr = frac (t.z);
  t.z -= fr; // floor(t.zw);
  float
Res;
  if (fr < 0.30)
    Res = tex2D_bilinear4x(tex, t.xyzz, Scales);
  else if (fr > 0.70)
    Res = tex2D_bilinear4x(tex, t.xyzz + float4 (0, 0, 1, 1),
                           Scales * float2 (0.5, 2));
  else {
    Res = tex2D_bilinear4x(tex, t.xyzz, Scales);
    float Res1 = tex2D_bilinear4x(tex, t.xyzz + float4 (0, 0, 1, 1),
                                  Scales * float2 (0.5, 2));
    fr = saturate ((fr - 0.30) * (1 / (0.70 - 0.30)));
    Res = Res1 * fr + Res * (1 - fr);
  }
  return Res;
}


Listing 18-3. Extending the Bilinear Filtering Technique to Trilinear Filtering

 

밉 레벨의 영향력이 둘 다 중요한 영역에서만 두 개의 텍스처 색인을 수행함으로써 더 최적화된 삼선형 텍스처 fetch 를 가지게 되었음에 주목하라. 다른 영역에서 우리는 가장 가까운 밉 레벨에 대한 LOD 값을 "그냥 긁어 온다(snap)". 즉 텍스처 대역폭을 절약할 수 있다.

 

Avoiding Unnecessary Work with Branching

 

최적화된 텍스처 필터링을 사용함에도 불구하고 물 렌더링을 하는 동안 텍스처 fetch 의 개수는 여전히 많을 수 있으며, 이것은 성능에 많은 영향을 끼친다. 렌더링되는 정점의 개수를 줄일 수도 있지만, 그것은 전체적인 가시적 디테일을 낮추고 위신호(aliasing, 화면이 고르지 못함)를 증가시킬 수 있다.

 

우리는 많은 batch 를 가진 기하도형을 가지고 물을 렌더링하고 있기 때문에, 일부 삼각형들은 완전히 바깥으로 나가버린다. 그러한 삼각형에 대해서 조차도, 정점 프로그램은 계속해서 실행되고 있기 때문에, 계산이 필요한 비용이 많이 드는 리소스를 낭비하고 있다는 데 주의하라. 카메라 프러스텀 외부에 있는 삼각형에 대한 계산을 건너 뛴다면 정점 당 작업의 양을 많이 줄일 수 있다.

 

정점 프로그램은 한 번에 하나의 정점에 대해 연산을 하고, 위상(topological) 정보 에 접근하지 않는다. 그래서 우리는 삼각형 수준이 아니라 단지 하나의 정점 수준에서만 결정을 내릴 수 있다. 이것은 삼각형 안의 일부 정점이 정점 텍스처링을 건너 뛰고 나머지는 그렇지 않을 때 인공물을 생성할 수 있다. 경험을 통해 우리는 삼각형과 정점 텍스처 displacement 가 이런 인공물이 나타나지 않을 만큼 충분히 작다는 것을 발견했다.

 

다음 의사코드는 이 아이디어를 설명한다 :

float4 ClipPos = mul (ModelViewProj, INP);
float3 b0 = abs (ClipPos.xyz) < (ClipPos.www * C0 + C1);

if ( all (b0)) {
  
// 정점은 가시 삼각형에 속한다,
  // 적절하게 텍스처 샘플링과 정점 변환을 수행한다
}

위의 코드에서 우리는 clip-space 정점 위치를 사용해서 현재 정점이 프러스텀 내부에 있는지 여부를 검사하고, 필요할 때만 비용이 많이 드는 계산을 수행했다.

 

C0 과 C1 은 특별한 "거짓(fudge)" 상수인데, 이는 얼마나 많은 삼각형이 카메라 프러스텀 뒤에어 확장되어 클리핑을 수행해야 하는지를 제어한다. 그러한 방식으로 우리는 삼각형이 가시화되어 있지만 프러스텀 외부에 있는 정점에 대한 텍스처링 건너 뜀에 의해 발생하는 인공물을 제거했다. 우리는 효율적으로 "클리핑" 프러스텀을 약간 넓게 구성해서 스크린 모서리를 따라 존재하는 특정한 양의 "테두리 영역(guard-band)" 공간을 허용했다. 우리의 물 면이 충분히 세분화되어 있고 정점 텍스처 displacement 가 합리적이기 때문에, 이러한 간단한 기법은 실용적으로 잘 작동한다.

 

Using Render-to-Texture

 

또한 우리는 높이 맵 텍스처를 단일 부동 소수점 텍스처로 개별 패스를 통해 결합함으로써 접근 속도를 증가시킬 수 있다. 그러면 그것은 정점 쉐이더 냉서 불필요한 다중의 고비용 필터링 연산을 수행할 필요가 없어지게 된다. 부가적으로 이제 우리는 더욱 작은 텍스처 포맷인 16 비트 부동 소수점 포맷을 원래 높이맵의 저장공간을 위해 사용할 수 있다. 또한 애니메이션되는 일련의 높이맵을 3D 텍스처의 조각으로서 저장할 수도 있다. 이것은 애니메이션을 더욱 부드럽게 만든다.

 

이러한 최적화를 사용해 우리의 렌더링 루프는 두 개의 패스를 가지게 되었다 :

  1. 특별한 픽셀 쉐이더를 사용해서 단일 4변형 텍스처를 fp32 텍스처로 렌더링함으로써 높이맵을 결합한다. 이 텍스처의 텍셀은 원형 메시의 정점에 매핑된다. 
  2. 생성된 높이 맵을 위에 설명한 것처럼 원형 메시 정점을 변형하기 위한 정점 텍스처로서 사용한다.

 

Back Sides of Waves

 

우리 조명 계산은 물이 평평하다는 전제하에서 픽셀 쉐이더에서 수행되기 때문에, 이 근사치는 특정한 경우 가시적 인공물을 생성할 수 있다.

 

이러한 경우 Figure 18-4 에 나온 것처럼 기하학적 displacement를 위해서 웨이브가 관찰자의 반대쪽을 가리키고 있다고 해도 우리는 웨이브의 뒷면을 보게 되며, 그것은 현실감있게 보이지 않는다. 이것은 웨이브의 위쪽에 왜곡된 밝은 영역을 만들게 된다.

 

이러한 인공물을 최소화하기 위해서, 우리는 조명 계산을 위해 사용되는 법선 벡터를 관찰자 쪽으로 약간 "기울임(tilt)"으로써 법선벡터를 수정한다. 그러면 그것들은 웨이브의 앞쪽 면에 더욱 가깝게 될 것이다. 동봉한 CD 에서 이 기법을 위한 소스 코드를 찾아볼 수 있다. Figure 18-5 는 이 장에서 설명된 기법을 사용해 생성된 신을 보여 준다.

 

 


 
Figure 18-4. A Source of Rendering Artifacts
The back side of a wave (green) may be shaded even though it shouldn't be visible. Adjusting the normal vector used in the lighting calculation can significantly reduce this error.



 
Figure 18-5. Extremely Realistic Water Rendered with Techniques Presented Here

 

18.2.5 Rendering Local Perturbations

 

종종 부유물이나 물 속으로 떨어지는 오브젝트에 의해서 발생하는 지역적인 흔들림(choppiness)을 렌더링하고자 할 때가 있다. 이것은 게임에서 특히 중요한데, 폭발, 배의 흔적과 같은 것들을 생성할 필요가 있다. 우리의 물 표면에 대한 높이맵 기반 모델에서 물리적으로 정확한 기법을 통합하는 것은 매우 어렵기 때문에, 우리는 경험에 기반한 더 간단한 기법에 대해 논의하도록 하겠다. 

 

Analytical Deformation Model

 

지역적인 흔들림을 표현하기 위한 가장 간단한 방법은 변형된 정점 위치를 수학적으로(anlaytically) 왜곡하는 것이다. 정점 쉐이더 내에 계산된 정점 위치를 사용해 그것들을 혼합한다. 폭발을 예로 들면 우리는 다음과 같은 공식을 작성할 수 있다 :

 

여기에서 r 은 물 평면에서의 폭발 중심으로부터의 거리이고, b 는 제거 상수이다(decimation constant). I0, w, k 값은 주어진 폭발과 그 파라미터에 의해 선택된다.

 

렌더링을 위해 우리는 정규 물 렌더링을 위해서 사용했던 것과 동일한 원형 그리드를 사용할 수 있지만, 폭발 위치에 중심이 맞춰져야 한다.

 

Dynamic Displacement Mapping

 

논리적으로 생성되는 displacement 를 직접적으로 정점 텍스처로 렌더링하기 위한 다른 선택은 본질적으로 GPU 상에서 범용 목적 프로그래밍 유형(general-purpose programming on the GPU, GPGPU)의 접근을 구현한다. 그 방식으로 우리는 첫 번째 패스에서 정점 텍스처를 생성하고, 다음 패스에서 실제 물 렌더링을 위해 그것을 사용한다. 부가적인 이익이 있다면, 우리는 기본 높이 맵을 필터링하고 픽셀 쉐이더 내에서 "octaves"를 합침으로써 정점 쉐이더로부터 일부 작업을 언로드할 수 있다.

 

displacement 를 계산하기 위해서 우리는 위에서 언급된 analytical 모델을 사용하거나 프레임에서 프레임으로 지역 displacment 를 호출함으로써 cellular-automate 접근의 사용을 시도할 수 있다. 적절한 방향으로 텍스처를 블러링함으로써 바람 효과도 구현할 수 있다.

 

그러나 1 km 의 물 표면을 50 cm 해상도로 다루게 된다면, 그것은 필수적으로 2048 x 2048 의 해상도를 사용하게 될 것이며, 텍스처 메모리와 쉐이더 실행 속도에 부가적인 압박을 가하게 될 것이다. 또한 빠른 관측 위치의 변화는 문제가 될 소지가 있다.

 

그럼에도 불구하고 이러한 접근을 사용해 실험해 보라고 독자들에게 권고하고자 한다.

 

Foam Generation

 

흔들림이 충분히 강해지면 우리는 사실성을 더욱 증가시키기 위해서 거품을 생성할 수 있다. 가장 간단한 방법은 특정 높이 H0 위에 있는 정점에 이미 생성되어 있는 거품 텍스처를 사용해 블렌딩하는 것이다. 거품 텍스처의 투명도는 다음 공식을 사용해서 계산된다 :

여기에서 H 는 거품이 최대치인 위치의 높이이며, H0 은 바닥 높이이다. H 는 현재 높이이다.

 

거품 텍스처는 거품 생성과 분해의 전개를 보여주기 위해서 애니메이션될 수 있다. 애니메이션 과정은 아티스트에 의해서 수작업으로 생성될 수도 있고 프로그램에서 생성할 수도 있다.

 

18.3 Conclusion

 

정점 텍스처 fetch 의 유연성과 동적 브랜칭(branching) 의 성능 절약 기능을 합침으로써 우리는 적절한 속도로 사실감있게 보이는 물 표면을 렌더링하기 위한 실용적 기법을 개발할 수 있었다. 여기에서 기술한 접근은 Pacific Fighters 에 성공적으로 적용되었으며, 그것은 광범위한 영역의 사실적인 물 표면을 생성할 수 있게 해 준다 - 10 cm 에서 40 km. 이것은 현대의 비행 시뮬레이터에도 적합하다. 우리는 물 표면의 전체 가시 영역에서의 타일 형태의 인공물을 제거할 수 있었다.

 

미래의 하드웨어는 이 기법의 더욱 더 감칠맛 나는 구현을 가능하게 만들어 줄 것이며, 특히 텍스처 값에 대해서 수작업으로 필터링을 수행해야할 필요를 제거해 줄 것이며, 또한 정점 쉐이더 성능을 더욱 높여 줄 것이다.

 

우리가 사용한 접근법의 품질은 거친(fine-grained) 디테일 및 물 표면의 범프를 제공하기 위해 parallax 매핑과 같은 진보된 쉐이딩 기법을 적용함으로써 더욱 증가될 수 있다. 8 장의 "Per-Pixel Displacement Mapping with Distance Functions," 를 참조하여 이러한 접근에 대해서 보기 바란다.

 

마지막으로 조명 계산은 매우 동적인 영역의(high-dynamic-range) 기법으로부터 매우 많은 이득을 얻을 수 있다. 왜냐하면 매우 반사를 많이 하는 물 표면은 매우 거대한 밝기 변화를 보여줄 수 있기 때문이다.

 

18.4 References

 

Fournier, Alain, and William T. Reeves. 1986. “A Simple Model of Ocean Waves.” In Computer Graphics (Proceedings of SIGGRAPH 86), pp. 75-84.

Kryachko, Yuri. 2004. “Modelling Sea and River Water with Pixel Shader 2.0.” Presentation. Available online at
http://www.kriconf.ru/2004/rec/KRI-2004.Programming_20.ppt

 

Tessendorf, Jerry. 2001. “Simulating Ocean Water.” In “Simulating Nature: Realistic and Interactive Techniques,” SIGGRAPH 2001 course notes, course 47.

'대학생 졸업하기 전 레벨 > DirectX' 카테고리의 다른 글

d3dx9d_40.dll, cgD3D9.dll, cg.dll  (0) 2009.03.18
최근 하고 있는 맵툴 작업입니다...  (1) 2009.03.16
물 렌더링 (쉐이더)  (0) 2009.03.15
쉐이더 에러시  (0) 2009.03.07
D3DX 조명  (0) 2007.12.03
Direct 3D 프로그래밍 과제  (0) 2007.11.12

쉐이더 사용시 아래와 같은 메세지가 나올때가 있다.

 error: x3539:ps_1_x is not supported in this veion of the complier

혹은
 error: x3539:ps_1_x is no longer supported use /Gec in fxc to automatically upgrade to ps_2_0
나의 회사 피시 그래픽 카드가 geforce 8600gt 인데 이것은 쉐이더 각각 3.0을 지원한다.

이것으로 아래 예제를 실행하면 위와 같은 에러가 난다. 에러 설명처럼 플래그를 찾아서 해결하였는데 다음과 같다.

 

D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY 플래그가 /Gec와 같다.

 

사용법은 다음과 같이 쓰면 된다. 그러면 쉐이더 1.x버젼에서 자동으로 2.0대 버젼으로 교체해준단다.

 

 hr = D3DXCreateEffectFromFile( g_pd3dDevice,
                             "dx9_hlsl_fx_simple.fx",
                             NULL,
                             NULL,
                             D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,  //이부분
                             NULL,
                             &g_pEffect,
                             &pBufferErrors );

'대학생 졸업하기 전 레벨 > DirectX' 카테고리의 다른 글

최근 하고 있는 맵툴 작업입니다...  (1) 2009.03.16
물 렌더링 (쉐이더)  (0) 2009.03.15
쉐이더 에러시  (0) 2009.03.07
D3DX 조명  (0) 2007.12.03
Direct 3D 프로그래밍 과제  (0) 2007.11.12
픽킹. Projection 변환  (0) 2007.11.11

+ Recent posts