Xna 4.0 – Podstawowe shadery zgodne z WP7 – Environment Mapping


Aby wyjaśnić na czym polega efekt zacznę od dwóch zrzutów ekranu z jego implementacją na wygenerowanym torusie.

image image

Idea jest dobrze wyjaśniona na Wikipedii. Generalnie chodzi o skonstruowanie materiału, który odbija światło (i inne obiekty) jak na przykład powierzchnia metalu. Xna realizuje “kubusiowe” podejście 🙂 czyli oparte na sześcianie i sześciu teksturach, które w zależności od kątów są wykorzystane do odbić.

Za pomocą Xna 4.0 proces przypisania takiego efektu do obiektu jest banalnie prostu.
Jedynym warunkiem na wejściu jest utworzenie (lub wgranie z pliku) obiektu, który ma określone koordynaty UV (do mapowania tekstury). W przypadku tego efektu jest to wymagane, sam się na tym przejechałem, kiedy nie chciałem mieć żadnego podstawowego mapowania tekstur pod spodem i określiłem tylko to co wydawało mi się konieczne do environment mappingu i w efekcie dostałem wyjątek wewnętrzny po stronie Xna i zwieszkę emulatora WP7 🙂

Mając taki model jak na przykład wygenerowany przeze mnie torus powyżej określenie efektu odbywa się podobnie jak w poprzednim przykładzie:

EnvironmentMapEffect effect = new EnvironmentMapEffect(device);

effect.Texture = baseTexture; //wymagana tekstura w formacie Texture2D
effect.EnvironmentMap = envMap; //wymagana tekstura w formacie TextureCube
effect.EnvironmentMapSpecular = new Vector3(.4f, .4f, .4f);
//podbicie “mocy” oświetlenia, popatrzcie różnicę pomiędzy lewym
//a prawym zrzutem ekranu. Pierwszy był bez manipulacji wartością Specular.

Przypisanie takiego efektu (czego nie omówiłem wcześniej) do obiektu to prosta sprawa:

Effect effect = GetEnvMapEffect(GraphicsDevice);
//powyższa metoda w praktyce wykonuje powyższy kod i zwraca efekt
sphere.Meshes[0].MeshParts[0].Effect = effect;

Jeśli geometrię wygenerowana mamy samemu to zanim ją zaczniemy rysować należy wykonać metodę Apply() dla każdego przejścia (EffectPass) we wszystkich technikach (lub CurrentTechnique) danego efektu. We wszystkich przejściach i technikach to tak naprawdę na wyrost powiedziane. To się przydaje gdy efekt mamy zdefiniowany za pomocą Shadera napisanego w HLSL. W przypadku efektów zdefiniowanych w klasach i zgodnych z WP7 technikę będziemy mieli jedną i przejście jedno więc wystarczy na skróty:

effect.CurrentTechnique.Passes[0].Apply();

W przypadku klasy Model to wszystko dzieje się automatycznie gdy wykonamy Model.Draw().

Jeśli jesteście ciekawi co taki environment mapping robi dokładniej to macie przykład takiego samego efektu właśnie napisanego w HLSL (zgodny z PC i Xbox 360):

#define PI 3.1415
//parametry wejściowe
float4x4    mWorldViewProj;        // World * View * Projection
float3       mCameraPosition;
texture     mTexture;
texture     mEnvTexture;

float        eyePositionW;

samplerCUBE mEnvTextureSample = sampler_state
{
    texture = <mEnvTexture>;
    magfilter = LINEAR;
    minfilter = LINEAR;
    mipfilter = LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler gTextureSampler = sampler_state
{
    texture = <mTexture>;
    mipfilter = LINEAR;
};

struct VS_IN
{
    float4 Position : POSITION;
    float3 Normal    : NORMAL;
};

// -----------------------------------------------------------------
struct VS_OUT {
    float4 pos : POSITION;   
    float4 texNorm : TEXCOORD0;
    float4 envCoord : TEXCOORD1;      
};

float4 reflect(float4 I, float4 N)
{
    return I - 2.0 * N * dot(N, I);
}

VS_OUT Vertex_Shader_Transform(
    in float4 vPosition : POSITION,
    in float4 vNormal :NORMAL,
    in float4 vTexCoord : TEXCOORD0       
    )
{
    VS_OUT outVal;
    float4 normalized = normalize(vNormal);

    outVal.pos = mul( vPosition, mWorldViewProj );

    outVal.texNorm = float4(0,0,0,0);
    outVal.texNorm.x = asin(normalized.x)/(PI);
    outVal.texNorm.y = asin(normalized.y)/(PI);       

    float4 positionW = mul(mWorldViewProj, outVal.pos);
    float4 N = mul(mWorldViewProj, vNormal);
    N = normalize(N);

    float4 I = positionW-eyePositionW;
    outVal.envCoord = reflect(I, N);

    return outVal;
}
// -----------------------------------------------------------------
float4 PixelShaderFunction(
    float reflectionFactor : COLOR,
    VS_OUT input,
    uniform sampler TextureMap ) : COLOR0
{

    float4 reflectedColor = texCUBE(mEnvTextureSample, input.envCoord);
    float4 outColor = (reflectedColor);
    outColor.a = 1.0;
    return outColor;
}
// -----------------------------------------------------------------
technique Technique1
{
    pass p0
    {       
        VertexShader = compile vs_2_0 Vertex_Shader_Transform();
        PixelShader = compile ps_2_0 PixelShaderFunction( gTextureSampler );
    }
}

Comments (0)

Skip to main content