HLSL学习笔记(一) —— 旋转的三角形

毕业设计选的是HLSL编程相关的,于是开始学习。

理论方面的不介绍了,这方面资料很多。只是记录一些编程实践相关的东西。

首先要准备一个HLSL程序运行的基本框架。这里我不使用MS提供的DXUT,太过复杂且使你对底层的来龙去脉懵懵懂懂,DX方面的龙书《Introduction to 3D Game Programming with DirectX 9.0》介绍了一个框架十分简洁明了,但考虑到以后我的程序的复杂度和可扩展性我使用一个面向对象的DX程序框架,主要参考《DX实时渲染技术详解》(kelly dempski著)。基本有4个文件

1.Application.h 提供一个基本的CHostApplication类 主要包括 初始化D3D,创建设备,创建窗口,枚举模式等基本函数以及 场景初始化,渲染,丢失,销毁的虚函数。其中最主要的是提供一个Go()函数,这是处理消息循环,场景渲染的主函数。 包括的成员变量有 D3D对象,D3D设备对象,窗口句柄,窗口长宽等。

2.Application.cpp 实现CHostApplication类提供的成员函数。

3.Executable.h  提供2个函数声明。程序入口函数及消息处理回调函数。

4.Executable.CPP 程序执行主体。在main()中创建CHostApplication派生类的对象,之后进入消息循环及场景渲染主体Go().

一些基本的D3D初始化及窗口函数在CHostApplication基类中已经实现,我们只要将重心移到CHostApplication的派生类,在派生类中重写CHostApplication基类的渲染初始化,渲染操作,结束渲染等函数用以实现特定场景的绘制工作。

下面介绍这个HLSL程序:旋转的三角形。

1.在VertexApplication.h中。构建CHostApplication类的派生类 CVertexApplication

 #include "Application.h"

class CVertexApplication : public CHostApplication 
{
public:
 BOOL FillVertexBuffer();
 BOOL CreateVertexBuffer();
 void DestroyVertexBuffer();
 CVertexApplication();
 virtual ~CVertexApplication();

 //This function will be called after the D3D object is
 //created.  Here we'll actually create the vertex buffer.
 virtual BOOL PostInitialize();

 //This function will clean up the vertex buffer prior to
 //the destruction of the device
 virtual BOOL PreTerminate();

 //These functions will ensure that lost vertex buffers are
 //recreated once the device is reset
 virtual BOOL PreReset();
 virtual BOOL PostReset();

 //This function will actually render the data.
 virtual void Render();

 //This is the pointer to our vertex buffer
 LPDIRECT3DVERTEXBUFFER9 m_pVertexBuffer;
 IDirect3DVertexShader9* m_pVertexShader;
 LPD3DXCONSTANTTABLE m_pConstantTable_VS;

 D3DXMATRIXA16 m_matWorld,m_matView,m_matProj;
};

注意最后我添加了用于构建HLSL程序的顶点渲染器对象及常量表。

2. 在VertexApplication.cpp 文件中实现上述相关函数。给出关键代码

BOOL CVertexApplication::PostInitialize()
{
 if (FAILED(EasyCreateWindowed(m_hWnd, D3DDEVTYPE_HAL,
          D3DCREATE_HARDWARE_VERTEXPROCESSING)))
  return FALSE;

    // 创建顶点缓冲区

if(!CreateVertexBuffer())
  return FALSE;

    // 填充顶点缓冲区

 if(!FillVertexBuffer())
  return FALSE;

    //编译顶点渲染器代码
 LPD3DXBUFFER pCode_VS;
 DWORD dwShaderFlags = D3DXSHADER_SKIPOPTIMIZATION|D3DXSHADER_DEBUG;
    if(FAILED((D3DXCompileShaderFromFile( L"HLSLTransform.vsh", NULL, NULL, "VS",
                                     "vs_2_0", dwShaderFlags, &pCode_VS,
                                     NULL, &m_pConstantTable_VS))))
    return FALSE;

 //创建顶点渲染器
    if(FAILED((m_pD3DDevice->CreateVertexShader((DWORD*)pCode_VS->GetBufferPointer(),
                                      &m_pVertexShader ))))
    return FALSE;

    pCode_VS->Release();

 //构造观察矩阵
 D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-5 );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMatrixLookAtLH( &m_matView, &vEyePt, &vLookatPt, &vUpVec );

 //构造投影矩阵
 float fAspectRatio = (float)m_WindowWidth/m_WindowHeight;
    D3DXMatrixPerspectiveFovLH( &m_matProj, D3DX_PI/4, fAspectRatio, 1.0f, 100.0f );
  
 //关闭光照处理, 默认情况下使用光照处理
    m_pD3DDevice->SetRenderState( D3DRS_LIGHTING, false );

 //设置剔出模式,为不剔出任何面
 m_pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

 return TRUE;
}

以上是渲染场景前的初始化操作。编译了HLSL代码并创建了顶点渲染器对象。

下面是场景绘制代码

void CVertexApplication::Render()
{
 //Here we regenerate the data and draw the points
 //FillVertexBuffer();

    HRESULT hr = 0;
 //建立世界矩阵
 UINT  iTime  = timeGetTime() % 2000;
    FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 2000.0f;
 D3DXMatrixIdentity( &m_matWorld );
    D3DXMatrixRotationY( &m_matWorld, fAngle );

 //设置全局变量
 D3DXMATRIX mWorldViewProj = m_matWorld * m_matView * m_matProj;
 m_pConstantTable_VS->SetMatrix( m_pD3DDevice, "matWorldViewProj", &mWorldViewProj);

 //设置顶点渲染器对象
 hr = m_pD3DDevice->SetVertexShader(m_pVertexShader);
 if(FAILED(hr))
  return;
 
 // 绘制三角形
 m_pD3DDevice->SetStreamSource(0,m_pVertexBuffer,0,sizeof(SIMPLE_VERTEX));
 m_pD3DDevice->SetFVF(D3DFVF_SIMPLEVERTEX);
 m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 1);
}

在绘制场景的过程中,通过不断改变世界矩阵而改变mWorldViewProj 局部变量值,然后绑定该变量到HLSL代码文件中的全局变量matWorldViewProj,我们可以先来看看HLSL代码文件:

//==============================================================
// Desc: 顶点渲染器代码
//==============================================================

//坐标变换矩阵
float4x4 matWorldViewProj; 

//--------------------------------------------------------------
// Desc: 输出结构
//--------------------------------------------------------------
struct VS_OUTPUT
{
   float4 Pos   : POSITION;
   float4 Color : COLOR;
};

//--------------------------------------------------------------
// Desc: 顶点渲染
//--------------------------------------------------------------
VS_OUTPUT VS( float4 Pos: POSITION, float4 Color: COLOR )
{
   VS_OUTPUT Out = (VS_OUTPUT) 0;
   Out.Pos = mul(Pos, matWorldViewProj);
   Out.Color = Color;
   return Out;
}

可以看到,对于固定流水线中顶点缓存中的每个顶点的位置POSITION和颜色COLOR作为输入参数传递给VS函数,然后将顶点位置通过Pos*matWorldViewProj达到变换顶点坐标的效果之后返回该顶点坐标,而HLSL代码文件中的matWorldViewProj全局变量绑定到了固定流水线中mWorldViewProj这个局部变量的地址,随着mWorldViewProj的改变(mWorldViewProj每一帧都在变化),matWorldViewProj同样改变着,故而使顶点坐标达到了变换的效果,之后在顶点缓存区中的顶点坐标为变换后的坐标,将之Draw出来即可。

效果图:

完整的工程文件地址:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值