Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Aby wyjaśnić na czym polega efekt zacznę od dwóch zrzutów ekranu z jego implementacją na wygenerowanym torusie.
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 );
}
}