Autor Wątek: [solved] XNA - Oświetlenie punktowe - problem z shaderem  (Przeczytany 2947 razy)

Offline Chimerian

  • Użytkownik
    • Profil na Warsztat.GD

# Grudzień 18, 2012, 19:39:20
Witam. Mam następujący problem nad którym siedzę już od pewnego czasu i nie umiem znaleźć rozwiązania. Problem związany jest prawdopodobnie z błędnie interpretowanymi osiami współrzędnych. Na początek kod programu:

Rysowanie obiektów wykonuję w następujący sposób:
        void DrawObjects(GameTime gameTime)
        {
            for (int i = 0; i < objects.Count; i++)
            {
                //DrawObjectWithStandartEffects(i);
                DrawModelWithPointLight(i);
            }
        }

        void DrawModelWithPointLight(int i)
        {
            pointLightEffect.CurrentTechnique = pointLightEffect.Techniques["PointLight"];

            foreach (ModelMesh mesh in models[objects[i].modelID].Meshes)
            {
                    foreach (ModelMeshPart part in mesh.MeshParts)
                    {
                        Matrix world = Matrix.CreateScale(objects[i].scale) *
                                       Matrix.CreateRotationX(objects[i].rotationX) *
                                       Matrix.CreateRotationY(objects[i].rotationY) *
                                       Matrix.CreateRotationZ(objects[i].rotationZ) *
                                       Matrix.CreateTranslation(objects[i].position);
                       
                        Matrix swiatlo = Matrix.CreateTranslation(lights[0].position);

                        part.Effect = pointLightEffect;
                        pointLightEffect.Parameters["LightPosition"].SetValue(lights[0].position);
                        pointLightEffect.Parameters["LightPower"].SetValue(1);
                        pointLightEffect.Parameters["xAmbient"].SetValue(0.05f);
                        pointLightEffect.Parameters["ColorMap"].SetValue(objects[i].texture);
                        pointLightEffect.Parameters["World"].SetValue(world * mesh.ParentBone.Transform);
                        pointLightEffect.Parameters["xWorld"].SetValue(world);
                        pointLightEffect.Parameters["View"].SetValue(camera.view);
                        pointLightEffect.Parameters["Projection"].SetValue(camera.projection);
                    }
                    mesh.Draw();
                }
            }         
        }

        void DrawObjectWithStandartEffects(int i)
        {
            // Draw the model.
            foreach (ModelMesh mesh in models[objects[i].modelID].Meshes)
            {               
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = Matrix.CreateScale(objects[i].scale) *
                                   Matrix.CreateRotationX(objects[i].rotationX) *
                                   Matrix.CreateRotationY(objects[i].rotationY) *
                                   Matrix.CreateRotationZ(objects[i].rotationZ) *
                                   Matrix.CreateTranslation(objects[i].position);
                    effect.View = camera.view;
                    effect.Projection = camera.projection;

                    effect.EnableDefaultLighting();
                }

                mesh.Draw();
            }
        }

oraz kod shadera:

float4x4 World;
float4x4 View;
float4x4 Projection;

float4x4 xWorld;
float3 LightPosition;
float LightPower;
float xAmbient;

Texture ColorMap;
sampler ColorSampler = sampler_state
{
texture = <ColorMap>;
magfilter = LINEAR;
minfilter = LINEAR;
mipfilter = LINEAR;
AddressU = mirror;
AddressV = mirror;
};

struct VertexInput
{
float4 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoords : TEXCOORD0;
};

struct VertexOutput
{
    float4 Position     : POSITION;   
    float2 TexCoords    : TEXCOORD0;
    float3 Normal       : TEXCOORD1;
    float3 Position3D   : TEXCOORD2;
};

struct PixelOutput
{
    float4 Color        : COLOR0;
};


float DotProduct(float3 lightPos, float3 pos3D, float3 normal)
{
    float3 lightDir = normalize(pos3D - lightPos);
    return dot(-lightDir, normal);   
}


VertexOutput VertexShader(VertexInput input)
{
    VertexOutput output = (VertexOutput)0;
       
    float4x4 worldViewProjection = mul(mul(World, View), Projection);
    output.Position = mul(input.Position, worldViewProjection);
   
    output.TexCoords = input.TexCoords;
   
    output.Normal = normalize(mul(input.Normal, (float3x3)xWorld)); 
     
    output.Position3D = mul(input.Position, xWorld);

    return output;
}

PixelOutput PixelShader(VertexOutput input)
{
    PixelOutput Output = (PixelOutput)0;   

    float diffuseLightingFactor = DotProduct(LightPosition, input.Position3D, input.Normal);
    diffuseLightingFactor = saturate(diffuseLightingFactor) * LightPower;
    float4 baseColor = tex2D(ColorSampler, input.TexCoords);
    Output.Color = baseColor * (diffuseLightingFactor + xAmbient);

    return Output;
}

technique PointLight
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VertexShader();
        PixelShader = compile ps_2_0 PixelShader();
    }
}


Okej, to teraz przystąpię do omówienia problemu. Chodzi o to, że gdy korzystam ze standardowych efektów wbudowanych w XNA (funkcja DrawObjectWithStandartEffects), obiekty wyświetlają się prawidłowo na swoich miejscach, natomiast w przypadku gdy zaczynam używać shadera z oświetleniem punktowym (funkcja DrawModelWithPointLight), osie wydają się być wywrócone. Gdy kod programu (w funkcji DrawModelWithPointLight) zamienię z:

                        Matrix world = Matrix.CreateScale(objects[i].scale) *
                                       Matrix.CreateRotationX(objects[i].rotationX) *
                                       Matrix.CreateRotationY(objects[i].rotationY) *
                                       Matrix.CreateRotationZ(objects[i].rotationZ) *
                                       Matrix.CreateTranslation(objects[i].position);

na:

                        Matrix world = Matrix.CreateScale(objects[i].scale) *
                                       Matrix.CreateRotationX(objects[i].rotationX) *
                                       Matrix.CreateRotationY(objects[i].rotationY) *
                                       Matrix.CreateRotationZ(objects[i].rotationZ) *
                                       Matrix.CreateTranslation(new Vector3(objects[i].position.X, -objects[i].position.Z, objects[i].position.Y));

to obiekty wyświetlają się prawidłowo (są na tych samych miejscach, co przy użyciu BasicEffect). Cóż - jest to częściowe rozwiązanie problemu i w dodatku zrealizowane na siłę (a nie o to chodzi aby rozwiązać problem w sposób "brutalny" - chcę zrozumieć istotę problemu).

Co do samego światła - to działa prawidłowo jedynie na obiekty znajdujące się na osi X (gdy y = 0 i z = 0 dla światła i oświetlanych obiektów). W innych przypadkach pojawiają się przekłamania, co zapewne ma związek z błędną interpretacją osi.

Prosiłbym o pomoc - czy ktoś może mnie nakierować co robię źle ? To moje początki z hlsl, więc możliwe że błąd jest trywialny, niestety nie umiem znaleźć miejsca jego występowania.

EDIT:
XNA 3.1 / VS 2008 Express
« Ostatnia zmiana: Grudzień 19, 2012, 14:48:35 wysłana przez Chimerian »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Chimerian

  • Użytkownik
    • Profil na Warsztat.GD

# Grudzień 19, 2012, 13:07:16
Rozwiązałem problem. Kod Shadera był prawidłowy, natomiast wysyłałem zły parametr World:

Błędny kod:
pointLightEffect.Parameters["World"].SetValue(world * mesh.ParentBone.Transform);
Poprawiony:
pointLightEffect.Parameters["World"].SetValue(world);
Teraz wszystko działa - obiekty wyświetlają się tam gdzie powinny.