Some time ago we talked about this and ways to achieve it. Because this is not as simple as it sounds I'm going to show everyone how to do it, or at least the way I did it.
Note that the method I present here would be useful for such things as randomly creating foliage quads around a player in a FPS shooter much like Joint Operations. It will also work for special cloud effects, nebula effects, smoke effects, etc.
The problem:
- Create a randomly generated starfield around a point in 3D space
- The starfield must react to camera pitch, roll, and yaw.
- The starfield must be able to move reflecting the current vector of the camera.
- The starfield must always be present, thus it must regenerate itself.
- The starfield must use a single vertex buffer
The main problem here is that you need to generate your vertices in model space, not world space. This way you can test easily for when the vertices are too far from center and need to be regenerated.
The simple solution is to generate the vertices around 0,0,0 using a max radius to determine distance from 0,0,0. The next step is to 'move' those vertices to the camera position. So you translate by cameraX,cameraY, and cameraZ.
Now if you run the code you would have a nice starfield around your camera in all directions. The last part is moving the vertices and testing for regeneration conditions.
To move the stars you simply move them in model space by the negative camera look vector mutliplied by a speed multiplied by frame delta. Note that just because the actual camera is moving at say 10 or 20 units does not mean your starfield has to. Regen condition would be when x*x+y*y+z*z = radius_squared. Then you simply regenerate the star somewhere around the camera. You may think to only regen in front of the camera but this brings an uneven distribution of vertices in front of the camera which is very noticeable when the viewpoint changes.
Your starfield is not really part of the world, but it appears to be because of the translation. When you translate the starfield to camera position, here is what happens:
If you have a star with vertex at 0,0,0 and a camera at 100,100,100 - after translation the star is really at 100,100,100, but remains at 0,0,0 in your vertex buffer. So by translating the star to the camera position, you actually make the camera position the center of the world or 0,0,0.
Code:
#pragma once
#include "CStarXApp.h"
#include "CCamera.h"
#define STARVERTEX_FVF D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR
struct StarVertex
{
D3DXVECTOR3 vecPos;
D3DCOLOR dwDiffuse;
D3DCOLOR dwSpecular;
static const DWORD FVF;
StarVertex():vecPos(D3DXVECTOR3(0.0f,0.0f,0.0f)),dwDiffuse(0),dwSpecular(0) { }
StarVertex(float x,float y,float z,UINT uDiffuse,UINT uSpecular):
vecPos(D3DXVECTOR3(x,y,z)),dwDiffuse(uDiffuse),
dwSpecular(uSpecular) { }
};
class CStars
{
protected:
CCamera *m_spCamera;
IDirect3DDevice9 *m_spDevice;
StarVertex m_Stars[3000];
int m_iMaxZ2;
int m_iMaxZ;
void Render();
void GenerateStar(int iIndex);
D3DXMATRIX m_matRot;
public:
CStars(void) { }
virtual ~CStars() { }
void Create(IDirect3DDevice9 *pDevice,int iMaxZ);
void Update(float fZDelta,float fTimeDelta,D3DXVECTOR3 vecCameraPos);
void SetCamera(CCamera *pCamera) {m_spCamera=pCamera;}
void SetRotMatrix(float rx,float ry,float rz);
};
Code:
void CStars::Render()
{
m_spDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
m_spDevice->SetFVF(STARVERTEX_FVF);
m_spDevice->DrawPrimitiveUP(D3DPT_POINTLIST,500,&m_Stars,sizeof(StarVertex));
}
void CStars::Create(IDirect3DDevice9 *pDevice,int iMaxZ)
{
m_spDevice=pDevice;
m_iMaxZ=iMaxZ;
m_iMaxZ2=iMaxZ<<1;
for (int i=0;i<3000;i++)
{
GenerateStar(i);
}
}
void CStars::Update(float fZDelta,float fTimeDelta,D3DXVECTOR3 vecCameraPos)
{
StarVertex *ptrVert=&m_Stars[0];
int x=0,y=0,z=0;
D3DXVECTOR3 vecMove;
m_spCamera->GetLook(&vecMove);
int result=0;
for (int i=0;i<3000;i++,ptrVert++)
{
ptrVert->vecPos-=vecMove*fZDelta*fTimeDelta;
float fDist=D3DXVec3Length(&ptrVert->vecPos);
if (fDist>=(float)m_iMaxZ)
{
x=-m_iMaxZ+ rand()%m_iMaxZ2;
y=-m_iMaxZ+ rand()%m_iMaxZ2;
z=-m_iMaxZ+ rand()%m_iMaxZ2;
ptrVert->vecPos=D3DXVECTOR3((float)x,(float)y,(float)z);
ptrVert->dwDiffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
ptrVert->dwSpecular=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
}
}
D3DXMATRIX matTrans;
D3DXMatrixTranslation(&matTrans,vecCameraPos.x,vecCameraPos.y,vecCameraPos.z);
m_spDevice->SetTransform(D3DTS_WORLD,&matTrans);
Render();
}
void CStars::GenerateStar(int iIndex)
{
int x=-m_iMaxZ+ rand()%m_iMaxZ2;
int y=-m_iMaxZ+ rand()%m_iMaxZ2;
int z=-m_iMaxZ+ rand()%m_iMaxZ2;
m_Stars[iIndex].vecPos=D3DXVECTOR3((float)x,(float)y,(float)z);
m_Stars[iIndex].dwDiffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
m_Stars[iIndex].dwSpecular=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
}
Note that there are several more effects for this starfield such as making them alpha blended line segments, alpha and/or intensity based on distance from camera, etc, etc.