Информатика и образование
  Мобильная версия сайта            
               
[Главная] [Новости]
[Статьи]
[Проекты]
[Ссылки]
[Автор]
               
    [Архив новостей]        
               
  [Форум] на форуме можно задать вопрос, посмотреть ответы на часто задаваемые вопросы  
       
  Здравствуйте! Вы попали на информационно-образовательный сайт посвященный информатике, информационным технологиям и компьютерным играм. Подробнее о целях и задачах сайта в разделе Главная. [English version of this page here...]    
       
  [Базовые уроки по DirectX] [Основы DirectMusic на Delphi] [Основы DirectInput8 на Delphi] [Основы DirectSound8 на Delphi]    
  [Разработка компьютерной игры] [Пример игры Donuts3D] [Delphi DirectX]    
       
  Основы 3D-программирования DirectX8.1 в Delphi 6-7: теоретические и практические основы создания игр.    
       
  Вывод моделей с использованием шейдеров    
       
 

10.01.2008

Эти заметки написаны с целью лучшего уяснения работы с шейдерами, а также для внедрения использования анимации в имеющийся код.

   
       
  [Все уроки]    
       
  За основу взят пример mdoscene.    
       
  Что необходимо для отрисовки модели с использованием шейдера?    
       
  Для начала нужно объявить необходимые типы и константы    
       
 

const

{...}

VertexFormat = D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1;

type

PVertex = ^TVertex;
TVertex = packed record
vPos: TD3DXVector3;
vNorm: TD3DXVector3;
fTex: array[0..1] of Single;
end;

   
       
 

В описании класса приложения для модели описываются следующие необходимые поля:

m_matSeaFloor: TD3DXMatrix; // матрица трансформации модели
m_pSeaFloor: CD3DMesh;
// модель
m_pSeaFloorVB: IDirect3DVertexBuffer8;
// ее вершинный буфер
m_pSeaFloorIB: IDirect3DIndexBuffer8;
// ее индексный буфер (индексы - это номера тройки вершин, определяющие треугольные грани)
m_dwNumSeaFloorVertices: Cardinal;
// число вершин модели
m_dwNumSeaFloorFaces: Cardinal;
// число граней модели

m_dwSeaFloorVertexShader: Cardinal;
// обработчики шейдера для его использования
m_dwSeaFloorVertexShader3: Cardinal;

   
       
  В методе создания приложения обнуляем все указанные поля    
       
 

constructor CMyD3DApplication.Create;
begin

{...}

m_pSeaFloor := nil;
m_pSeaFloorVB := nil;
m_pSeaFloorIB := nil;
m_dwSeaFloorVertexShader := 0;
m_dwSeaFloorVertexShader3:= 0;

{...}

end;

   
       
 

В методе OneTimeSceneInit приложения производим создание необходимых объектов:

{...}

m_pSeaFloor := CD3DMesh.Create;

{...}

   
       
  В методе инициализации приложения производим всю необходимую подготовку моделей (загрузку, генерацию и т.п.), а также создание и заполнение необходимых вершинных и индексных буферов.    
       
 

function CSubmarineD3DApp.InitDeviceObjects: HResult;

begin

{...}

if Failed(m_pSeaFloor.Create_(m_pd3dDevice, PChar(SeaFloorModel))) then Exit;

{...}

m_pSeaFloor.SetFVF(m_pd3dDevice, VertexFormat);
m_dwNumSeaFloorVertices := m_pSeaFloor.GetSysMemMesh.GetNumVertices;
m_dwNumSeaFloorFaces := m_pSeaFloor.GetSysMemMesh.GetNumFaces;

m_pd3dDevice.CreateVertexBuffer(m_dwNumSeaFloorVertices * sizeof(TVertex),
D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, m_pSeaFloorVB);

// Copy vertices for the seafloor mesh
m_pSeaFloor.GetSysMemMesh.GetVertexBuffer(pMeshSourceVB);
m_pSeaFloorVB.Lock(0, 0, PBYTE(pDst), 0);
pMeshSourceVB.Lock(0, 0, PBYTE(pSrc), 0);
Move(pSrc^,pDst^, m_dwNumSeaFloorVertices * sizeof(TVertex));
Randomize;
for i:= 0 to m_dwNumSeaFloorVertices-1 do
begin
pDst[i].vPos.y := pDst[i].vPos.y+(random(500)/500);
{pDst[i].vPos.y := pDst[i].vPos.y+random(5);}
//pDst[i].vPos.z := pDst[i].vPos.z+160;
pDst[i].fTex[0] := pDst[i].fTex[0] * SeaFloorTileCount;
pDst[i].fTex[1] := pDst[i].fTex[1] * SeaFloorTileCount;

pDst[i].vPos.x := pDst[i].vPos.x * 0.2;
pDst[i].vPos.y := pDst[i].vPos.y * 0.2;
pDst[i].vPos.z := pDst[i].vPos.z * 0.2;
end;
m_pSeaFloorVB.Unlock;
pMeshSourceVB.Unlock;
pMeshSourceVB:= nil;

m_pd3dDevice.CreateIndexBuffer(m_dwNumSeaFloorFaces * 3 * sizeof(WORD),
D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, m_pSeaFloorIB);

// Copy indices for the seafloor mesh
m_pSeaFloor.GetSysMemMesh.GetIndexBuffer(pMeshSourceIB);
m_pSeaFloorIB.Lock(0, 0, PBYTE(pDst), 0);
pMeshSourceIB.Lock(0, 0, PBYTE(pSrc), 0);
Move(pSrc^, pDst^, 3 * m_dwNumSeaFloorFaces * SizeOf(Word));
m_pSeaFloorIB.Unlock;
pMeshSourceIB.Unlock;
pMeshSourceIB:= nil;

{...}

end;

   
       
  В методе RestoreDeviceObjects приложения - создаем все зависимые от Direct3D-устройства объекты    
       
 

TileMeshTexAndRestore(m_pSeaFloor, bSeaFloorTiled, SeaFloorTileCount); //в данном случае вызывается специальная процедура для заполнения модели нужным количеством тайлов. В простейшем случае просто происходит вызов

m_pSeaFloor.RestoreDeviceObjects

{...}

RestoreRenderCausticsStates; //для краткости здесь записана данная процедура, внутри нее происходит установка нужных состояний отрисовки, создание и загрузка шейдеров

{...}

   
       
 

внутри процедуры RestoreRenderCausticsStates; происходит следующее:

// удаляем старый вершинный шейдер (если предварительно вызывается метод InvalidateDeviceObjects, то это // будет избыточным)

if (m_dwShader <> 0) then
m_pd3dDevice.DeleteVertexShader(m_dwShader);
m_dwShader := 0;

// создаем определение для вершинного шейдера нашей модели
dwVertexDecl[0] := D3DVSD_STREAM(0);
dwVertexDecl[1] := D3DVSD_REG(0, D3DVSDT_FLOAT3); // положение меша
dwVertexDecl[2] := D3DVSD_REG(3, D3DVSDT_FLOAT3); // нормаль
dwVertexDecl[3] := D3DVSD_REG(6, D3DVSDT_FLOAT2); // текстурные координаты
dwVertexDecl[4] := D3DVSD_END;

strTestVertexShader := 'Media\Shaders\add.vsh';

// загрузка и ассемблирование вершинного шейдера из файла
result := D3DUtil_CreateVertexShader(m_pd3dDevice, strTestVertexShader,
@dwVertexDecl, m_dwShader);
if FAILED(Result) then Exit;

   
       
  В методе нарушения (Invalidate) приложения удаляем вершинный шейдер    
       
 

function CSubmarineD3DApp.InvalidateDeviceObjects;

begin

{...}

if (m_dwShader <> 0) then
m_pd3dDevice.DeleteVertexShader(m_dwShader);
m_dwShader := 0;

{...}

end;

   
       
  В методе отрисовки (Render) приложения производим отрисовку с помощью шейдера -    
       
 

function CSubmarineD3DApp.Render;

begin

{...}

RenderCaustic;
// Render the scene
RenderScene;

{...}

end;

   
       
 

внутри процедуры RenderCaustic:

begin

{...}

m_pd3dDevice.SetVertexShaderConstant(22, fAmbientLight, 1);

// Render the seafloor
m_matWorldOfMesh := m_matSeaFloor;
SetMatWorldOfMesh;
m_pd3dDevice.SetTexture(0, IDirect3DTexture8(m_pSeaFloor.m_pTextures^[0]));
m_pd3dDevice.SetVertexShader(m_dwSeaFloorVertexShader3);
m_pd3dDevice.SetStreamSource(0, m_pSeaFloorVB, SizeOf(TVertex));
m_pd3dDevice.SetIndices(m_pSeaFloorIB, 0);
m_pd3dDevice.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,
m_dwNumSeaFloorVertices, 0, m_dwNumSeaFloorFaces);

{...}

// далее осуществляется второй проход для альфа-блендинга эффекта бликов
// блики используют второй набор текстурных координат которые были сгенерированы
// вершинным шейдером. Освещение происходит от источника созданного выше, но
// окружающий (ambient) свет отключается для предупреждения подсветки объектов снизу (например,
// нам не нужен эффект бликов появляющийся на нижней части живота дельфина)
// И наконец, цвет тумана устанавливается в черный, так что блики
// угасают на расстоянии.

// Turn on alpha blending
m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, 1);

m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

// Setup the caustic texture
m_pd3dDevice.SetTexture(0, m_pCurrentCausticTexture);

// Set ambient and fog colors to black
m_pd3dDevice.SetVertexShaderConstant(22, fAmbientDark, 1);

m_pd3dDevice.SetRenderState(D3DRS_FOGCOLOR, $00000000);

// Render the caustic effects for the seafloor
m_matWorldOfMesh := m_matSeaFloor;
SetMatWorldOfMesh;
m_pd3dDevice.SetVertexShader(m_dwSeaFloorVertexShader3);
m_pd3dDevice.SetStreamSource(0, m_pSeaFloorVB, sizeof(TVertex));
m_pd3dDevice.SetIndices(m_pSeaFloorIB, 0);
m_pd3dDevice.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,
m_dwNumSeaFloorVertices, 0, m_dwNumSeaFloorFaces);

{...}

end;

   
       
 

внутри процедуры RenderScene: происходят только необходимые трансформации, т.е. фактическая отрисовка производится внутри RenderCaustic

begin

{...}

D3DXMatrixScaling(m_matSeaFloor, 1.0, 1.0, 1.0);
D3DXMatrixTranslation(matTrans, 1.0, -30.0, 1.0);
D3DXMatrixMultiply(m_matSeaFloor, m_matSeaFloor, matTrans);

{...}

end;

   
       
  В методе анимации сцены (FrameMove) приложения производим анимацию с помощью шейдера (если таковая имеет место)    
       
 

function CSubmarineD3DApp.FrameMove

begin

{...}

AnimateCausticTex;

{...}

end;

   
       
 

И наконец самое интересное происходит внутри метода

procedure CSubmarineD3DApp.AnimateCausticTex;
{$WRITEABLECONST ON}
const
fLight: array[0..3] of Single = (0.0, 1.0, 0.0, 0.0);
fLightSubmarineSpace: array[0..3] of Single = (0.0, 1.0, 0.0, 0.0);
fDiffuse: array[0..3] of Single = (1.00, 1.00, 1.00, 1.00);
fAmbient: array[0..3] of Single = (0.25, 0.25, 0.25, 0.25);
fFog: array[0..3] of Single = (0.5, 50.0, 1.0/(50.0-1.0), 0.0);
fCaustics: array[0..3] of Single = (0.5, 0.5, 0, 0);//(0.05, 0.05, 0, 0);
{$WRITEABLECONST OFF}
var
tex: Cardinal;
vZero, vOne: TD3DXVector4;
matSubmarineInv, matCamera, mat, matTranspose,
matCameraTranspose, matViewTranspose, matProjTranspose: TD3DXMatrix;
begin
if @m_matWorldOfMesh = nil then D3DXMatrixIdentity(m_matWorldOfMesh);

// Animate the caustic textures
tex := Trunc((m_fTime*32)) mod 32;
m_pCurrentCausticTexture := m_pCausticTextures[tex];

// Some basic constants
vZero:= D3DXVector4(0.0, 0.0, 0.0, 0.0);
vOne:= D3DXVector4(1.0, 0.5, 0.2, 0.05);

fCaustics[2] := Sin(m_fTime)/8;
fCaustics[3] := Cos(m_fTime)/10;

D3DXMatrixInverse(matSubmarineInv, nil, m_matWorldOfMesh{m_matSubmarine});
D3DXVec4Transform(PD3DXVector4(@fLightSubmarineSpace)^, PD3DXVector4(@fLight)^, matSubmarineInv);
D3DXVec4Normalize(PD3DXVector4(@fLightSubmarineSpace)^, PD3DXVector4(@fLightSubmarineSpace)^);

// Vertex shader operations use transposed matrices
D3DXMatrixMultiply(matCamera, m_matWorldOfMesh{m_matSubmarine}, m_matView);
D3DXMatrixMultiply(mat, matCamera, m_matProject);
D3DXMatrixTranspose(matTranspose, mat);
D3DXMatrixTranspose(matCameraTranspose, matCamera);
D3DXMatrixTranspose(matViewTranspose, m_matView);
D3DXMatrixTranspose(matProjTranspose, m_matProject);

m_pd3dDevice.SetVertexShaderConstant( 0, vZero, 1);
m_pd3dDevice.SetVertexShaderConstant( 1, vOne, 1);
//m_pd3dDevice.SetVertexShaderConstant( 2, vWeight, 1);
m_pd3dDevice.SetVertexShaderConstant( 4, matTranspose, 4 );
m_pd3dDevice.SetVertexShaderConstant( 8, matCameraTranspose, 4);
m_pd3dDevice.SetVertexShaderConstant(12, matViewTranspose, 4);
m_pd3dDevice.SetVertexShaderConstant(19, fLightSubmarineSpace, 1);
m_pd3dDevice.SetVertexShaderConstant(20, fLight, 1);
m_pd3dDevice.SetVertexShaderConstant(21, fDiffuse, 1);
m_pd3dDevice.SetVertexShaderConstant(22, fAmbient, 1);
m_pd3dDevice.SetVertexShaderConstant(23, fFog, 1);
m_pd3dDevice.SetVertexShaderConstant(24, fCaustics, 1);
m_pd3dDevice.SetVertexShaderConstant(28, matProjTranspose, 4);
end;

   
       
  далее нужно рассмотреть как осуществляется анимация в примере SkinnedMesh    
       
  [Назад] [Все уроки]    
       
 

Обновления и новости о развитии Delphi DirectX проекта
смотри на сайтах:

http://www.megainformaticsite.pochta.ru

http://www.megainformatic.boom.ru

http://www.megainformatic.narod.ru

 

   
       
     
 

по всем вопросам пишите на megainformatic@mail.ru или оставьте сообщение на форуме

 
     
   Обмен ссылками  
     
     
             
 
 
         
(с) МЕГА ИНФОРМАТИК 2006-2009
Hosted by uCoz