Thread: Lighting a Direct3D 9 mesh sphere

  1. #1
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401

    Lighting a Direct3D 9 mesh sphere

    I'm lighting a sphere as described in the title with diffuse and specular lighting. I have used, individually and in conjunction, directional and point lighting, but each time I try, a strange effect occurs. The way the sphere vertices are lit depends on the angle I am looking at it from, and if you could see this you would appreciate how ridiculous it looks. Has anyone succeeded in lighting a mesh sphere before?

    Code:
    mSphere=Mesh.Sphere(device,1.0f,32,16);
    mSphere.ComputeNormals();
    
    SphereCol=new Material();
    SphereCol.Ambient=Color.DarkRed;
    SphereCol.Diffuse=Color.Red;
    SphereCol.Specular=Color.White;
    .
    .
    .
    device.Lights[0].Type=LightType.Directional;
    device.Lights[0].Specular=Color.White;
    device.Lights[0].Diffuse=Color.White;
    device.Lights[0].Position=new Vector3(0.0f,0.0f, -4.0f);
    device.Lights[0].Direction=new Vector3(0.0f,0.0f, 1.0f);
    device.Lights[0].Enabled=true;
    
    device.Material=SphereCol;
    mSphere.DrawSubset(0);
    As you can see from the attachments, the 90 degree shot has the shadow on the wrong side.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yes I have. Are you normalizing the normals?

  3. #3
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    And another shot.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    And this one shows it well.

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Here is the code I'm using. CPlanet uses a CSphere object. I separated them for rendering reasons and I'll spare you the details.

    CSphere.cpp
    Code:
    #define CSPHERE
    
    #include "d3dx9.h"
    #include "CQuickCom.h"
    #include "CFileLog.h"
    #include "d3dx9mesh.h"
    
    
    //Converts degrees to radians
    #define DEGTORAD(x)  (x*D3DX_PI/180.0f)
    
    struct CSphereVertex
    {
      D3DXVECTOR3 pos;
      D3DXVECTOR3 normal;
      float   u,v;
      static const DWORD FVF;  
    
      CSphereVertex(D3DXVECTOR3 tpos,D3DXVECTOR3 tnormal,float tu,float tv):
                    pos(tpos),normal(tnormal),u(tu),v(tv){}
    
    };
    
    class CSphere
    {
      
      protected:
        CFileLog                  SphereLog;
       
        IDirect3DDevice9          *Device;
        
        ID3DXMesh                 *SphereMesh;
    
        unsigned int              NumTris;
        unsigned int              NumFaces;
        unsigned int              NumVertices;
        unsigned int              NumVertsPerRow;
        unsigned int              NumVertsPerCol;
        
        float                     Radius;
    
      public:
        CSphere(void) {Device=NULL,NumTris=0,NumFaces=0,
          NumVertices=0,NumVertsPerRow=0,NumVertsPerCol=0,Radius=0.0f;};
        
        ~CSphere(void)
        {
           if (SphereMesh) SphereMesh->Release();
    
        }
    
        //Old deprecated function
        //void CreateSphere(IDirect3DDevice9 *_device,
        //                  float _radius,
        //                  unsigned int _faces,
        //                  float _texmag,
        //                  D3DXVECTOR3 &pos);
        
        void CreateMeshSphere(IDirect3DDevice9 *_device,
                              float _radius,
                              unsigned int _faces);
        
        ID3DXMesh *GetMesh(void) {return SphereMesh;};
    
        unsigned int GetNumTris(void) {return NumTris;};
        unsigned int GetNumVertices(void) {return NumVertices;};
       
    };
    
    #endif
    CSphere.h
    Code:
    #include "CSphere.h"
    
    
    
    const DWORD CSphereVertex::FVF=D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
    
    
    //Create mesh
    void CSphere::CreateMeshSphere(IDirect3DDevice9 *_device,float _radius,unsigned int _faces)
    {
      //Class vars
      Device=_device;
      Radius=_radius;
      NumFaces=_faces;
    
      //Create temp mesh
      LPD3DXMESH temp;
      if (FAILED(D3DXCreateSphere(Device,Radius,NumFaces,NumFaces,&temp,NULL)))
      {
        ::MessageBox(0,"Failed to create sphere",0,0);
      }
    
      //Clone mesh with new FVF
      if (FAILED(temp->CloneMeshFVF(D3DXMESH_SYSTEMMEM,CSphereVertex::FVF,Device,&SphereMesh)))
      {
        ::MessageBox(0,"Failed to clone mesh",0,0);
      }
    
      //Lock vertex buffer
      CSphereVertex *Vertices;
      SphereMesh->LockVertexBuffer(0,(void **)&Vertices);
    
      //Setup mapping vars
      float angleinc_alpha=360.0f/_faces;
      float angleinc_beta=180.0f/_faces;
      float alpha=0.0f;
      float beta=-180.0f;
      float u=0.0f,v=0.0f;
    
      unsigned int vertex_count;
      
      //Set 2*PI - no mul in for loop
      float PI2=D3DX_PI*2.0f;
    
      //Map U,V coords
      //Produces very good results for 2:1 texture ratios
      for (unsigned int i=0;i<SphereMesh->GetNumVertices();i++)
      {
        u=(DEGTORAD(alpha)+D3DX_PI)/(PI2);
        v=DEGTORAD(beta)/D3DX_PI;
    
        Vertices[i].u=u;
        Vertices[i].v=v;
    
        alpha+=angleinc_alpha;
        vertex_count++;
        if (vertex_count>=(_faces))
        {
          alpha=0.0f;
          beta+=angleinc_beta;
          
          //Disabled test area
          //Vertices[i+1].v=0.0f;
          //Vertices[i+1].u=0.0f;
          
          vertex_count=0;
        }
      }
      //Unlock vertex buffer
      SphereMesh->UnlockVertexBuffer();
    
      //Discard temp mesh
      temp->Release();
      
    }
    CPlanet.cpp
    Code:
    #include "CPlanet.h"
    
    
    //Constructor - self explanatory
    CPlanet::CPlanet(void)
    {
      Device=NULL;
      Pos=D3DXVECTOR3(0.0f,0.0f,0.0f);
      Rotation=D3DXVECTOR3(0.0f,0.0f,0.0f);
      RotationVelocity=D3DXVECTOR3(0.0f,0.0f,0.0f);
      Texture=NULL;
      BumpMap=NULL;
      Radius=0.0f;
    }
    
    //Destructor - release COM objects - decrement their reference counts
    CPlanet::~CPlanet(void)
    {
      if (Texture)  Texture->Release();
      if (BumpMap)  BumpMap->Release();
    }
    
    //Set planet texture
    void CPlanet::SetTexture(std::string PlanetFile)
    {
      if (FAILED(D3DXCreateTextureFromFile(Device,PlanetFile.c_str(),&Texture)))
      {
        ::MessageBox(0,"Failed to load planet texture",0,0);
        return;
      }
    }
    
    //Set bump map -> fake bump mapping not per-pixel
    void CPlanet::SetBumpMap(std::string PlanetFile)
    {
      if (FAILED(D3DXCreateTextureFromFile(Device,PlanetFile.c_str(),&BumpMap)))
      {
        ::MessageBox(0,"Failed to load planet texture",0,0);
        return;
      }
    }
    
    //Create the model -> call CSphere::CreateMeshSphere()
    void CPlanet::CreateModel(IDirect3DDevice9 *_Device,float _Radius,int _Faces,D3DXVECTOR3 &pos)
    {
      Device=_Device;
      Radius=_Radius;
      Faces=_Faces;
      SetPosition(&pos);
      
    
      Planet.CreateMeshSphere(Device,Radius,_Faces);
    }
    
    
    //Disabled - unsure of atmospheres at this time
    void CPlanet::SetAtmosphere(D3DMATERIAL9 *_material,float _radius)
    {
      //AMaterial=*_material;
      //ARadius=_radius;
      
      //AtmosphereFlag=true;
    
      //Atmosphere.CreateMeshSphere(Device,ARadius,Faces);
    }
    
    //Render object
    void CPlanet::Render(float timeDelta,D3DXVECTOR3 cameraPos)
    {
      
      //Set U,V wrapping to true - must do this to get rid of render artifacts
      Device->SetRenderState(D3DRS_WRAP0,true);
      Device->SetRenderState(D3DRS_WRAP1,true);
      
      
      
      
      //Change model rotation based on velocity presets and time from last frame
      Rotation.x+=(RotationVelocity.x*timeDelta);
      Rotation.y+=(RotationVelocity.y*timeDelta);
      Rotation.z+=(RotationVelocity.z*timeDelta);
      
      //Setup world rotation matrix
      D3DXMATRIX rot;
      D3DXMatrixRotationYawPitchRoll(&rot,Rotation.x,Rotation.y,Rotation.z);
    
      //Setup translation -> no mul here
      rot._41=Pos.x;
      rot._42=Pos.y;
      rot._43=Pos.z;
    
      //Set transform
      Device->SetTransform(D3DTS_WORLD,&rot);
      
      //Set material
      D3DXVECTOR3 distvect;
      D3DXVec3Subtract(&distvect,&Pos,&cameraPos);
    
      float length=D3DXVec3Length(&distvect);
    
      
      float actualdist=10000.0f-(length-8000.0f);
      if (actualdist>0.0f)
      {
    
        AlphaValue=actualdist/10000.0f;
      } else AlphaValue=0.0f;
    
      
      if (AlphaValue>=1.0f) AlphaValue=1.0f;
      if (AlphaValue<=0.0f) AlphaValue=0.0f;
      
      Material.Diffuse.a=AlphaValue;
      Device->SetMaterial(&Material);
      
      //Set texture
      //Device->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_DIFFUSE);
      //Device->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
      Device->SetTexture(0,Texture);
    
      //Check pointer -> prob disabled on release build
      if (!Planet.GetMesh())
      {
        ::MessageBox(0,0,"Null pointer in mesh",0);
      }
    
      //Render object
      if (FAILED(Planet.GetMesh()->DrawSubset(0)))
      {
        ::MessageBox(0,0,"Cannot draw mesh",0);
      }
      
      
      //Restore render states
      Device->SetRenderState(D3DRS_WRAP0,false);
      Device->SetRenderState(D3DRS_WRAP1,false);
      //Device->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_DISABLE);
      //Device->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
      
    }
    CPlanet.h
    Code:
    
    #ifndef CPLANET
    #define CPLANET
    
    #include "CPrimitives.h"
    #include "d3dx9.h"
    #include <String>
    #include "CQuickCom.h"
    #include "CSphere.h"
    
    
    class CPlanet
    {
      //Device interface pointer
      IDirect3DDevice9    *Device;
      
      //Sphere object
      CSphere             Planet;
      
      //Model properties
      D3DXVECTOR3         Pos;
      D3DXVECTOR3         Rotation;
      D3DXVECTOR3         RotationVelocity;
      
      
      //Textures
      IDirect3DTexture9   *Texture;
      IDirect3DTexture9   *BumpMap;
      
      
      //Materials
      D3DMATERIAL9        Material;
      D3DMATERIAL9        AMaterial;
    
        
      float               Radius;
      float               ARadius;
      
      int                 Faces;
      bool                AtmosphereFlag;
    
      public:
        float               AlphaValue;
        
        CPlanet(void);
        ~CPlanet(void);
    
        void SetTexture(std::string PlanetFile);
        void SetBumpMap(std::string PlanetFile);
    
        
        void SetPosition(D3DXVECTOR3 *_newpos) {Pos=*_newpos;};
        void GetPosition(D3DXVECTOR3 *_curpos) {*_curpos=Pos;};
        
        void SetRotationParams(D3DXVECTOR3 *_rotation,D3DXVECTOR3 *_rotation_velocity)
        {
          Rotation=*_rotation;
          RotationVelocity=*_rotation_velocity;
        }
        
        void GetRotationParams(D3DXVECTOR3 *_rotation,D3DXVECTOR3 *_rotation_velocity)
        {
          *_rotation=Rotation;
          *_rotation_velocity=RotationVelocity;
        }
        
        void CreateModel(IDirect3DDevice9 *_Device,float _Radius,int _Faces,D3DXVECTOR3 &pos);
        
        void TestCreateModel(IDirect3DDevice9 *_device,float _radius,unsigned int _faces)
        {
          Device=_device;
          Radius=_radius;
          Faces=_faces;
    
          
          Planet.CreateMeshSphere(_device,_radius,_faces);
          D3DXComputeNormals(Planet.GetMesh(),NULL);
    
        }
    
        void SetMaterial(D3DMATERIAL9 *_material) {Material=*_material;};
        void GetMaterial(D3DMATERIAL9 *_material) {*_material=Material;};
        float GetRadius(void) {return Radius;};
        float GetAtmosphereRadius(void) {return ARadius;};
        void SetAtmosphere(D3DMATERIAL9 *_material,float _radius);
        void Render(float timeDelta,D3DXVECTOR3 cameraPos);
        void TestRender(float timeDelta);
    };
    
    #endif
    Hope that helps.

    Not the fastest way to do things and I'm reworking the code to use a much better rendering system, but you get the idea.
    This is the most important section:

    Code:
    void TestCreateModel(IDirect3DDevice9 *_device,float _radius,unsigned int _faces)
        {
          Device=_device;
          Radius=_radius;
          Faces=_faces;
    
          
          Planet.CreateMeshSphere(_device,_radius,_faces);
          D3DXComputeNormals(Planet.GetMesh(),NULL);
    
        }
    You must tell Direct3D to compute the normals for your mesh. This is a simple (upper left vertex is vnum -> clockwise from there)

    Code:
    D3DXVECTOR v1=Vertex[vnum+1]-Vertex[vnum];
    D3DXVECTOR v2=Vertex[vnum+2]-Vertex[vnum+1];
    D3DXVECTOR Cross;
    D3DXVec3Cross(&Cross,&v1,&v2);
    D3DXVec3Normalize(&Cross,&Cross);
    
    //No normal averaging
    Vertex[vnum].normal=Cross;
    Vertex[vnum+1].normal=Cross;
    Vertex[vnum+2].normal=Cross;
    But D3DX does this for you.

    There is an easier way to compute the normals on a sphere. Since all points are equidistant from the center the normal will always be the normalized distance between the vertex on the sphere and the center of the sphere. Since your sphere's are centered around 0,0,0 in model space you can simply normalize the Vertex to get the normal. You are essentially then clamping the vertex to the surface of a unit sphere.

    Code:
    D3DXVECTOR3 vNormal;
    D3DXVec3Normalize(&vNormal,Vertex[vnum]);
    Vertex[vnum].normal=vNormal;
    Note that this is per vertex normals, not per triangle face. So this sets your system up quite nicely to do some per-pixel dot3 lighting in a shader.

    Code:
    mSphere=Mesh.Sphere(device,1.0f,32,16);
    mSphere.ComputeNormals();
    I wouldn't do it that way. There is a possibility that even though your code works your sphere object might be going out of scope upon returning from Mesh::Sphere(). Also you might be creating a COM resource leak.
    Last edited by VirtualAce; 02-07-2005 at 09:50 PM.

  6. #6
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Thanks heaps for the code, I haven't found anything nearly as good on the net. Unfortunately it's a bit above my level so it will take some time to work out.

    As for the scope and resource leak issues, remember I'm using C#, everything should technically be managed for me. I think I will however go back to using C++. The only reason I was using C# was to learn a .NET language, but I do sorely miss the satisfaction of good C++ code.

    Once I've finished I'll let you know.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  7. #7
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    From the screenies it looks like the camera is rotating, but the light source is still in the same spot which should give the effect shown in those screenies. You sure this is not the case?
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  8. #8
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    The light is fixed and the camera is moving around in a circle. Maybe the pictures don't illustrate the problem, but believe me if you ran the program you would see what I mean. I can post the binary if you trust me not to infect you.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  9. #9
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    How 'bout posting both the binary and the source and someone could give a more accurate solution.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    First people want the source, not the binary. Now they want the binary not just the source. Sheesh.

    Sorry cannot post the binary. It's way over the board limit.

  11. #11
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I see that binary message was for him not me. Sorry.

    Anyways if your camera is moving around the object and the object is part of your world then it is really the object that is moving, not the camera. Your calculations must be in world space not view space. It seems to me that they are in view space.

    The camera is moving in space and rotating but to show this you really must rotate the world and move the world, not the camera. You always translate the camera back to 0,0,0 or the origin of the world since the camera is assumed to always be the origin of your world. Everything else is relative to the camera.

  12. #12
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I think my code does what your saying but I'm not sure. Here it is:
    Code:
                CamLookAt.X=(float)Math.Sin((double)angle)+CamPos.X;
                CamLookAt.Z=(float)Math.Cos((double)angle)+CamPos.Z;
    
                device.Transform.World=Matrix.Identity;
                device.Transform.View=Matrix.LookAtLH(CamPos, CamLookAt, new Vector3(0.0f,1.0f,0.0f) );
                
                device.Transform.Projection=Matrix.PerspectiveFovLH((float)Math.PI/4.0f,1.0f,1.0f,100f);
    EDIT: I keep reading your post and getting more confused. Can you give a quick example of the correct method?
    Last edited by bennyandthejets; 02-13-2005 at 05:30 PM.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Set your camera to always look at the same spot.

    This will compute the correct view matrix for a camera with 3 basis vectors..right, look, and up.

    Code:
    void Camera::getViewMatrix(D3DXMATRIX* V)
    {
      // Keep camera's axes orthogonal to eachother
      D3DXVec3Normalize(&_look, &_look);
      D3DXVec3Cross(&_up, &_look, &_right);
      D3DXVec3Normalize(&_up, &_up);
    
      D3DXVec3Cross(&_right, &_up, &_look);
      D3DXVec3Normalize(&_right, &_right);
    
      // Build the view matrix:
      float x = -D3DXVec3Dot(&_right, &_pos);
      float y = -D3DXVec3Dot(&_up, &_pos);
      float z = -D3DXVec3Dot(&_look, &_pos);
    
      (*V)(0,0) = _right.x; (*V)(0, 1) = _up.x; (*V)(0, 2) = _look.x; (*V)(0, 3) = 0.0f;
      (*V)(1,0) = _right.y; (*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y; (*V)(1, 3) = 0.0f;
      (*V)(2,0) = _right.z; (*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z; (*V)(2, 3) = 0.0f;
      (*V)(3,0) = x;        (*V)(3, 1) = y;     (*V)(3, 2) = z;       (*V)(3, 3) = 1.0f;
    }
    This is straight from Intro to 3D Game Programming by Frank Luna. The camera class in that book works extremely well.

    If you are just wanting a stationary camera then set it to look at the same spot at all times. I would have to see what is taking place in order to help you more.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Direct3D Alpha Blend with Lighting ?
    By rocketman03 in forum Game Programming
    Replies: 10
    Last Post: 07-07-2009, 04:04 PM
  2. Lighting in Managed Direct3d
    By Rune Hunter in forum Game Programming
    Replies: 0
    Last Post: 04-30-2006, 07:40 AM
  3. Mesh Rendering: Z-Buffer and Lighting
    By Epo in forum Game Programming
    Replies: 6
    Last Post: 04-20-2005, 07:11 AM
  4. Sphere code not working
    By VirtualAce in forum Game Programming
    Replies: 2
    Last Post: 10-03-2004, 07:29 AM
  5. need help with opengl lighting!!
    By genghis37 in forum Game Programming
    Replies: 1
    Last Post: 06-22-2002, 12:28 AM