XNA Creators Club Online
Page 1 of 1 (7 items)
Sort Posts: Previous Next

Parallax Occlusion Mapping - MDX

Last post 8/7/2009 2:54 PM by ShadowyCore. 6 replies.
  • 7/5/2007 10:56 PM

    Parallax Occlusion Mapping - MDX

    Hi, I'm hoping someone here can help me with parallax occlusion mapping in managed DX9. I'm using the ParallaxOcclusionMapping.fx which came with the DX SDK sample, and am trying to adapt the tutorial (which was in C++) to C#.

    The problem I'm having is that when running the parallax shader, everything comes out a sold colour:



    When I lower the LOD threshold variable in the shader to -1, it kind of works (I can see textures, but no parallax effect):



    I've looked at every parallax mapping tutorial I can find. The fact that my normal mapping shader (which is very similar) works fine but parallax doesn't (even though I haven't modified the .fx file from the SDK sample) is baffling. I'm hoping someone on here can take a look at my code and give me some clue as to what I might be doing wrong.

    My system has VS and PS 3.0 capabilities and the sample executable from the DX SDK (Apr 07) works fine. Other parallax mapping samples and tutorials from other people which I've downloaded also work fine.

    Here's code for three of my classes. The first is a fairly simple class for rendering...it contains all my DX code except for the code in the shaders and the code to load resources (which is in the resource buffer...I haven't included the source for that because I doubt it's the problem):

    using System.Windows.Forms;
    using Microsoft.DirectX.Direct3D;
    using Microsoft.DirectX;
    using System;
    using System.Drawing;
    using System.ComponentModel;
    using System.Collections;
    using Scene;
    using System.IO;

    namespace Rendering
    {
    class DX9RenderDevice : RenderDevice
    {

    private Form canvas = new Form(); //The windows form rendered to
    private Device device = null; //The DX device
    private PresentParameters pp = new PresentParameters();
    private DX9ResourceBuffer resources = null; //A class to store resources (meshes, textures etc in DX format)

    private ParallaxOcclusionShader shader; //My parallax occ. shader


    public DX9RenderDevice(int width, int height, bool fullscreen)
    {
    //Create the canvas and set the title
    canvas.ClientSize = new System.Drawing.Size(width, height);
    canvas.Text = "Indygo v0.0";

    buildRenderCapabilities();
    initialize(width, height, fullscreen);

    //Create the resource buffer
    resources = new DX9ResourceBuffer(device);

    shader = new ParallaxOcclusionShader(device);

    device.RenderState.Lighting = false;

    canvas.Show();
    }

    public override ResourceBuffer getResourceBuffer()
    {
    return resources;
    }
    //Called by each model in the scene
    public override void renderModel(Scene.Model model, Scene.Camera camera)
    {

    //Set up the shader variables
    shader.prepare(model, camera, resources, canvas);

    //My simple model format stores indexes for meshes, materials and textures in the resource buffer.
    int[] materialIndexes = model.getMaterialIndexes();
    int[] textureIndexes = model.getTextureIndexes();
    Mesh mesh = resources.getMesh(model.getMeshIndex());

    int passes = shader.Effect.Begin(0);
    for(int i = 0; i < passes; i++)
    {
    shader.Effect.BeginPass(i);

    for (int m = 0; m < materialIndexes.Length; m++)
    {
    int materialIndex = materialIndexes[m];
    int textureIndex = -1;
    if (textureIndexes.Length > m)
    textureIndex = textureIndexes[m];

    Material material;
    if (materialIndex < 0)
    material = new Material();
    else
    material = resources.getMaterial(materialIndex);

    device.Material = material; //platinum

    Texture texture = null;

    if (textureIndex >= 0)
    texture = resources.getTexture(textureIndex);

    device.SetTexture(0, texture); //platinum

    mesh.DrawSubset(m);
    }

    shader.Effect.EndPass();
    }

    shader.Effect.End();

    }



    public override void renderViewport(Camera camera, int worldX, int worldY, float width, float height)
    {
    Viewport view = new Viewport();
    view.X = worldX;
    view.Y = worldY;
    view.Width = (int)(width * canvas.Width);
    view.Height = (int)(height * canvas.Height);
    view.MinZ = 0;
    view.MaxZ = 1;

    device.Viewport = view;

    device.Transform.View = Matrix.LookAtLH(convertVector(camera.getPosition()),
    convertVector(camera.getTarget()),
    convertVector(camera.getUpVector()));
    device.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI / 4.0f), (float)canvas.Width/(float)canvas.Height,
    1.0f, 1000.0f);

    camera.getScene().getRootSceneNode().renderAll(this, camera);


    }

    public override void begin()
    {
    device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
    System.Drawing.Color.CornflowerBlue, 1.0f, 0);

    device.BeginScene();

    }

    public override void end()
    {
    device.EndScene();
    device.Present();
    }

    public bool buildRenderCapabilities()
    {
    try
    {
    Microsoft.DirectX.Direct3D.Caps caps = Manager.GetDeviceCaps(0, DeviceType.Hardware);

    capabilities.TransformAndLight = caps.DeviceCaps.SupportsHardwareTransformAndLight;
    capabilities.Pure = caps.DeviceCaps.SupportsPureDevice;

    return true;
    }
    catch (Exception e)
    {
    return false;
    }
    }

    public bool buildPresentParameters(int width, int height, bool fullscreen)
    {
    DisplayMode mode = Manager.Adapters[0].CurrentDisplayMode;

    Format adaptorFormat = fullscreen ? Format.X8R8G8B8 : mode.Format;

    if (Manager.CheckDeviceFormat(0, DeviceType.Hardware, adaptorFormat, Usage.DepthStencil,
    ResourceType.Surface, DepthFormat.D24S8))
    {
    pp.AutoDepthStencilFormat = DepthFormat.D24S8;
    }
    else if (Manager.CheckDeviceFormat(0, DeviceType.Hardware, adaptorFormat, Usage.DepthStencil,
    ResourceType.Surface, DepthFormat.D24X8))
    {
    pp.AutoDepthStencilFormat = DepthFormat.D24X8;
    }
    else if (Manager.CheckDeviceFormat(0, DeviceType.Hardware, adaptorFormat, Usage.DepthStencil,
    ResourceType.Surface, DepthFormat.D16))
    {
    pp.AutoDepthStencilFormat = DepthFormat.D16;
    }
    else
    {
    return false;
    }

    pp.BackBufferWidth = width;
    pp.BackBufferHeight = height;
    pp.BackBufferFormat = adaptorFormat;
    pp.BackBufferCount = 1;
    pp.MultiSample = MultiSampleType.None;
    pp.MultiSampleQuality = 0;
    pp.SwapEffect = SwapEffect.Discard;
    pp.DeviceWindowHandle = canvas.Handle;
    pp.Windowed = !fullscreen;
    pp.EnableAutoDepthStencil = true;
    pp.PresentFlag = PresentFlag.DiscardDepthStencil;
    pp.FullScreenRefreshRateInHz = fullscreen ? mode.RefreshRate : 0;
    pp.PresentationInterval = PresentInterval.Immediate;

    return true;
    }

    public bool initialize(int width, int height, bool fullscreen)
    {
    CreateFlags flags;

    if (capabilities.TransformAndLight)
    {
    flags = CreateFlags.HardwareVertexProcessing;

    if (capabilities.Pure)
    flags |= CreateFlags.PureDevice;
    }
    else
    flags = CreateFlags.SoftwareVertexProcessing;

    if (!buildPresentParameters(width, height, fullscreen))
    return false;

    try
    {
    device = new Microsoft.DirectX.Direct3D.Device(0, DeviceType.Hardware, canvas.Handle,
    flags, pp);
    device.DeviceReset += new System.EventHandler(this.OnResetDevice);
    this.OnResetDevice(device, null);

    }
    catch (DirectXException e)
    {
    return false;
    }

    return true;
    }



    public void OnResetDevice(object sender, EventArgs e)
    {


    Microsoft.DirectX.Direct3D.Device dev = (Microsoft.DirectX.Direct3D.Device)sender;

    // Turn on the zbuffer
    dev.RenderState.ZBufferEnable = true;

    // Turn on ambient lighting
    dev.RenderState.Ambient = System.Drawing.Color.White;



    }

    //Converts from my transform object to a DX Matrix
    public Matrix convertTransform(Util.Transform transform)
    {
    Matrix scale = Matrix.Scaling(transform.getScale().x(), transform.getScale().y(),
    transform.getScale().z());
    Matrix rotate = Matrix.RotationYawPitchRoll(transform.getRotation().y(), transform.getRotation().x(),
    transform.getRotation().z());
    Matrix translate = Matrix.Translation(transform.getPosition().x(), transform.getPosition().y(),
    transform.getPosition().z());

    return scale * rotate * translate;
    }

    //Converts from my Vector object to a DX Vector3
    public Vector3 convertVector(Util.Vector vector)
    {
    Vector3 toReturn = new Vector3(vector.x(), vector.y(), vector.z());
    return toReturn;
    }

    }
    }

    In the main method of another class I create three models and apply two textures to each of them...the first (texture 0) is the base texture, the second (texture 1) is the height map texture.

    This next class is my ParallaxOcclusionShader class:

    using Microsoft.DirectX.Direct3D;
    using Microsoft.DirectX;
    using Scene;
    using System;
    using System.Windows.Forms;

    namespace Rendering
    {
    class ParallaxOcclusionShader
    {

    private Effect shader;

    public Effect Effect
    {
    get { return shader; }
    }

    public ParallaxOcclusionShader(Device device)
    {
    shader = Effect.FromFile(device, "ParallaxOcclusionMapping.fx", null, null,
    ShaderFlags.None, null);
    }

    public void prepare(Model model, Camera camera, DX9ResourceBuffer resources, Form canvas)
    {
    Util.Transform world = model.getWorldTransform();
    Matrix matWorld = convertTransform(world);

    Matrix matView = Matrix.LookAtLH(convertVector(camera.getPosition()),
    convertVector(camera.getTarget()),
    convertVector(camera.getUpVector()));
    Matrix matProj = Matrix.PerspectiveFovLH((float)(Math.PI / 4.0f), (float)canvas.Width / (float)canvas.Height,
    0.1f, 1000.0f);

    Vector4 eyePos = new Vector4(camera.getPosition().x(), camera.getPosition().y(),
    camera.getPosition().z(), 1.0f);

    Matrix WorldViewProj = matWorld * matView * matProj;

    int[] materialIndexes = model.getMaterialIndexes();
    int[] textureIndexes = model.getTextureIndexes();
    Mesh mesh = resources.getMesh(model.getMeshIndex());

    Texture baseTexture = resources.getTexture(textureIndexes[0]);
    Texture heightTexture = resources.getTexture(textureIndexes[1]);

    shader.SetValue("g_baseTexture", baseTexture);
    shader.SetValue("g_nmhTexture", heightTexture);
    shader.SetValue("g_vEye", eyePos);
    shader.SetValue("g_mWorld", matWorld);
    shader.SetValue("g_mWorldViewProjection", WorldViewProj);
    shader.SetValue("g_mView", matView);

    shader.SetValue("g_materialAmbientColor", new Vector4(0.35f, 0.35f, 0.35f, 0.0f));
    shader.SetValue("g_materialDiffuseColor", new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
    shader.SetValue("g_materialSpecularColor", new Vector4(1.0f, 1.0f, 1.0f, 1.0f));

    shader.SetValue("g_fSpecularExponent", 120.0f);
    shader.SetValue("g_bAddSpecular", true);

    shader.SetValue("g_LightDir", Vector4.Normalize(new Vector4(0,0,1,0)));
    //shader.SetValue("g_LightDir", new Vector4((float)Math.Sin(Math.PI * 2 - Math.PI / 6),
    // 0.0f,
    // (float)-Math.Cos(Math.PI * 2 - Math.PI / 6),
    // 0.0f));
    shader.SetValue("g_LightDiffuse", new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
    //shader.SetValue("g_LightAmbient", new Vector4(0.35f, 0.35f, 0.35f, 0.0f));

    shader.SetValue("g_fBaseTextureRepeat", 1.0f);
    shader.SetValue("g_fHeightMapScale", 0.17f);

    shader.SetValue("g_bVisualizeLOD", false);
    shader.SetValue("g_bVisualizeMipLevel", false);
    shader.SetValue("g_bDisplayShadows", true);

    shader.SetValue("g_vTextureDims", new Vector4(512.0f, 512.0f, 0.0f, 0.0f));

    shader.SetValue("g_nLODThreshold", 5);
    shader.SetValue("g_fShadowSoftening", 0.58f);
    shader.SetValue("g_nMinSamples",10);
    shader.SetValue("g_nMaxSamples", 100);

    shader.Technique = "RenderSceneWithPOM";

    shader.CommitChanges();
    }

    public Matrix convertTransform(Util.Transform transform)
    {
    Matrix scale = Matrix.Scaling(transform.getScale().x(), transform.getScale().y(),
    transform.getScale().z());
    Matrix rotate = Matrix.RotationYawPitchRoll(transform.getRotation().y(), transform.getRotation().x(),
    transform.getRotation().z());
    Matrix translate = Matrix.Translation(transform.getPosition().x(), transform.getPosition().y(),
    transform.getPosition().z());

    return scale * rotate * translate;
    }

    public Vector3 convertVector(Util.Vector vector)
    {
    Vector3 toReturn = new Vector3(vector.x(), vector.y(), vector.z());
    return toReturn;
    }

    }
    }

    It's very similar to my NormalMappngShader class (which works flawlessly):

    private Effect shader;

    public Effect Effect
    {
    get { return shader; }
    }

    public NormalMappingShader(Device device)
    {
    shader = Effect.FromFile(device, "NormalMapping.fx", null, null,
    ShaderFlags.None, null);
    }

    public void prepare(Model model, Camera camera, DX9ResourceBuffer resources, Form canvas)
    {
    Util.Transform world = model.getWorldTransform();
    Matrix matWorld = convertTransform(world);

    Matrix matView = Matrix.LookAtLH(convertVector(camera.getPosition()),
    convertVector(camera.getTarget()),
    convertVector(camera.getUpVector()));
    Matrix matProj = Matrix.PerspectiveFovLH((float)(Math.PI / 4.0f), (float)canvas.Width / (float)canvas.Height,
    1.0f, 1000.0f);

    Vector4 eyePos = new Vector4(camera.getPosition().x(), camera.getPosition().y(),
    camera.getPosition().z(), 0.0f);

    Matrix WorldViewProj = matWorld * matView * matProj;
    Matrix WorldViewIT = matWorld;
    WorldViewIT.Invert();
    WorldViewIT.Transpose(matView);

    int[] materialIndexes = model.getMaterialIndexes();
    int[] textureIndexes = model.getTextureIndexes();
    Mesh mesh = resources.getMesh(model.getMeshIndex());

    Texture baseTexture = resources.getTexture(textureIndexes[0]);
    Texture heightTexture = resources.getTexture(textureIndexes[1]);

    shader.SetValue("ModelViewProj", WorldViewProj);
    shader.SetValue("ModelViewIT", WorldViewIT);
    shader.SetValue("ModelWorld", matWorld);
    shader.SetValue("lightPos", new Vector4(0.0f, 1.0f, -1.0f, 0.0f));

    shader.SetValue("texture0", baseTexture);
    shader.SetValue("texture1", heightTexture);
    }

    public Matrix convertTransform(Util.Transform transform)
    {
    Matrix scale = Matrix.Scaling(transform.getScale().x(), transform.getScale().y(),
    transform.getScale().z());
    Matrix rotate = Matrix.RotationYawPitchRoll(transform.getRotation().y(), transform.getRotation().x(),
    transform.getRotation().z());
    Matrix translate = Matrix.Translation(transform.getPosition().x(), transform.getPosition().y(),
    transform.getPosition().z());

    return scale * rotate * translate;
    }

    public Vector3 convertVector(Util.Vector vector)
    {
    Vector3 toReturn = new Vector3(vector.x(), vector.y(), vector.z());
    return toReturn;
    }

    Sorry for all the code...I hope it makes sense. I've uploaded my project to (http://www.sharebigfile.com/file/190992/Indygo-zip.html)...it's not really commented, and I'm just experimenting with different things at the moment so it's a bit messy, but you can execute it and see the problem for yourself.

    If anyone could give any help, it would be greatly appreciated...I suspect it's something wrong with my device creation...maybe I'm not setting a flag necessary to make it work...I'm confident I'm passing the values to the shader correctly.



  • 7/11/2007 12:54 AM In reply to

    Re: Parallax Occlusion Mapping - MDX

    Some new developments...I've rewritten the whole engine, hoping to isolate the problem by greatly simplifying the code. The problem still occurs. But now if I set the CreateFlags parameter to software vertex processing instead of hardware, it works.

    So, I guess now it's a more general question...what are some possible reasons a shader would work under software vertex processing and not under hardware? Especially when the exact same shader works using hardware vertex processing in the DX SDK samples?
  • 11/16/2007 6:59 AM In reply to

    Re: Parallax Occlusion Mapping - MDX

    Have you ever found a solution for this problem? Got the same problem right now and it drives me mad. MDX seems to mess anything up with the shader, but unmanged DX and XNA are able to handle it correctly. Very strage. Any help is appreciated.
  • 1/8/2008 1:01 AM In reply to

    Re: Parallax Occlusion Mapping - MDX

    Yeah, it was a long time ago now, but it turns out that I wasn't setting the normals correctly on the model. I think it was the normals anyway...may have been binormals or tangents, or something like that.

  • 7/9/2009 8:29 PM In reply to

    Re: Parallax Occlusion Mapping - MDX

    Could your graphics card not be up to it? Setting it to use a Software render might explain for that.
    Freefall Game Engine - In Development
  • 8/7/2009 8:25 AM In reply to

    Re: Parallax Occlusion Mapping - MDX

    It seems to me yoru one of the very very few people who have got this working in XNA.  Can you please help me get this working?

    plz email me r_jay_thompson@hotmail.com
  • 8/7/2009 2:54 PM In reply to

    Re: Parallax Occlusion Mapping - MDX

    Me? No. I never even tried this. There's a tutorial on Ziggyware that talks about it, but I never tried it.

    http://www.ziggyware.com/readarticle.php?article_id=47

    I just think that your graphics card is attempting to do something it can't, and so the textures/mapping don't display properly.
    Freefall Game Engine - In Development
Page 1 of 1 (7 items) Previous Next