第三章 2D Rendering Drawing a 2DTriangle(绘制三角形) 【已完成】【附带源码】

Drawing a 2D Triangle
DirectX11 : 进行渲染, 使我们这一路 斩荆棘破虫害的 核心目标.
Me : 那如何进行 渲染?
D : 我怎么知道!!
M : 呃...⊙▂⊙...
D : What do you ask?
M : Howto rander ?
D : 答案已在你心中.
M : 哈?
D : "howto" 那是 "如何进行操作", 那是 到处指手画脚的 context 专项工作, 寡人才不理会呢!
...
Context: 那还不简单, 也就那么几个步骤.
M : 愿闻其详.
C : 第一, 先要将黑板擦干净;
M :
这是当然的啦, 我来... ( 擦,擦,擦擦擦...)
C : 第二, 得告诉我, 绘制的目的是什么, 希望我用 楷体还是隶书, 有多少字体, 多大的黑板, 需要进行 电视录像同步播放不...
M:
哈? 怎么有这些要求? 您也太苛刻了吧!
C:
苛你的头, 万一要我骂 party, 宣传 法lun功, 那我怎能干! 要我写 你比Wilson-Loo 帅 我怎能干! 万一要写万言书...
M:
好吧. 我这次的主体是 "认真学习党的十八大指导精神"; 请务必使用 三号字体作为标题大小, 四号作为正文字体大小; 还有, 尽量使用 正楷吧, 现在又不是在立碑, 无需隶书; 行间距 1厘米就行; 最后, 黑板左边画图, 右边才是正文.
C: 第三, 把稿文 和 图片草图给我.
M:
对对对, 这就是啦; 就A4纸这么一张.
C: 第四, 需要我按照怎么的流程进行 工作, 连字编写, 单字规范编写?
M:
这是哪般?
C:
图形学上 绘制的三角形都有 trianglelist, trianglestrip 和 trianglefun 区分啦.
C: 第五, ... (停顿)
... ... ( 停顿了良久)
M:
第五步, 究竟是什么呀? 再不开始绘画, 花儿都谢了.
C:
等你呀.
M:
哈( 疯了吗)?
C:
万一我用了一小时做完了所有时期, 你又要去其他教室出黑板报怎么办?至少要我知道个大概计划吧.

M: 没有了, 马上工作.

 

DirectX11 中进行如下的绘制步骤:

// 第一步, 清除 渲染目标视图
float clearColor[ 4] = { 0.0f, 0.0f, 0.25f, 1.0f};
d3dContext_->ClearRenderTargetView( backBufferTarget, clearClor);

unsigned int stride = sizeof( VertexPos);
unsigned int offset = 0;

// 第二步, 设置 数据信息/及格式控制信息
d3dContext_->IASetInputLayout( inputLayout);

// 第三步, 制定 要进行绘制的 几何体信息
d3dContext_->IASetVertexBuffers( 0, 1, &vertexBuffer_, &stride, &offset);

// 第四步, 指明 如何绘制三角形
d3dContext_->IAsetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

d3dContext_->VSSetShader( solidColorVS, 0, 0);
d3dContext_->PSSetShader( solidColorPS, 0, 0);
d3dContext_->Draw( 3, 0);

// 第五步, 马上输出
swapChain_->Present( 0, 0);

 

 

IAsetInputLayout()context 绑定由 CreateInputLayout() 创建的 vertext layout .每次将要渲染几何体时都需使用特定input layout, 这要进行 IASetInputLayout();

IASetInputLayout() 仅仅需要使用 ID3D11InputLayout 对象一个参数.

IASetVertexBuffers() 函数用来设置一个或多个 vertex buffers, 原型如下:

void IASetVertexBuffers(
    UINT StartSlot,
    UINT NumBuffers,
    ID3D11Buffer *const *ppVertexBuffers,
    const UINT *pStrids,
    const UINT *pOffsets    
);

 

 IASetVertexBuffers() 函数原型如:

void IASetVertexBuffers(
    UINT StartSlot,
    UINT NumBuffers,
    ID3D11Buffer *const *ppVertexBuffers,
    const UINT *pStrids,
    const UINT *pOffsets    
);

 

第一个参数是要进行绑定的buff 索引, 因为 buffer 会有很多个, 这里需要进行指定;
第二个参数是指上述的 buffer 个数;
第三个参数就是存储这些buffer的地方;
第四个 和 第五个参数, 我暂时还不能确定

IAsetPrimitiveTopology() 函数用于告诉 Direct3D 采用哪种方式渲染几何体. 比如我们使用 D3D11_PRIMITIVE_TOPOLYGY_TRIANGLELIST 表明持用 triangle-list 方式进行渲染几何体;
想要使用 triangle-strips 方式时, 我们使用 D3D11_PRIMITIVE_TOPOLYGY_TRIANGLESTRIP;
总共大概有 42 方式进行渲染; 其中的绝大部分用来控制点 以获得更高级的几何体效果; 整个列表 可以在 DirectX文档中找到.

进行了设置这些 输入集input assembler 后, 还需要设置 shaders. 本书后面的章节里 我们会看到如何使用其他 类型的 shaders( 例如 geometry shaders); 但现在, 我们主要集中在 vertex shader 和 pixel shader.
使用 Direct3D context 的 VSSetShader() 函数 可以设置 vertex shader;
使用 PSSetShader() 可以设置 pixel shader.这两个函数的输入参数都是 将要进行设置的shader, 指向任何类型的class 实例, 和 这些class 实例的数量, 这些我们都会在 第七章 涉及 类实例接口时 在讨论.

一旦我们设置/绑定了 几何体所需的数据后, 最后一个是 调用 Draw() 函数进行绘制.Draw() 函数隶属于 context, 带两个参数, vertex的个数 和 开始绘制的 vertex的索引位置, 该位置可以使 vertext buffer中任何你想要 开始进行绘制的 位置.

 

上面的 swapChain_->Present( 0, 0); 将 输出已经渲染好厚的 图片 输出到显示器.

2D Triangle Demo

下面, 我们来设计 “绘制三角形demo”: 在屏幕上绘制一个简单的三角形。 

Loading the Geometry(加载几何体)

从这第三章以来, 我们讨论过, 为了能够渲染几何体, 我们需要如下几点:

1. vertex buffer

2. vertext buffer 使用的描述 vertext layout 的 input layout

3.一系列的 shader

自动 Direct3D10 开始, Direct3D 渲染图形图像中 shader 成为一项不可缺少的 基本要求, 本demo 将会涉及到 vertex shader(顶点着色器) 和 pixel shader(像素着色器), 这两个shader 在本demo中仅仅是 渲染出一个固定颜色的平面 surface rendered with a solid color;本章后面我们还会看到如何扩展这些效果到 “映射纹理图片到 表面”上。

demo 类 TriangleDemo 定义在 TriangleDemo.h 头文件中, 添加了以下成员:

private:
    ID3D11VertexShader *solidColorVS_;
    ID3D11PixelShader *solidColorPS_;
    ID3D11InputLayout *inputLayout_;
    ID3D11Buffer *vertexBuffer_;

下面是该头文件 TriangleDemo.h

#include "Dx11DemoBase.h"

class TriangleDemo : public Dx11DemoBase
{
public:
    TriangleDemo();
    virtual ~TriangleDemo();

    bool LoadContent();
    void UnloadContent();

    void Update( float dt);
    void Render();

private:
    ID3D11VertexShader    *solidColorVS_;
    ID3D11PixelShader    *solidColorPS_;

    ID3D11InputLayout    *inputLayout_;
    ID3D11Buffer        *vertexBuffer_;
};

 

三角形的 顶点vertex , 我们将采用 简单三维 浮点数结构体, 该结构体是来自 XNA Math 库的 XMFLOAT3. 在 应用程序结束的时候, TriangleDemo 类的成员, 这些对象都必须通过 Release() 函数进行释放。释放操作将在TriangleDemo::UnloadContent(). 

下面是 TriangleDemo 顶点结构, 构造函数 , 析构函数 和 卸载资源部分 的定义:

#include "TriangleDemo.h"
#include <xnamath.h>

struct VertexPos
{
    XMFLOAT3 pos;
};

TriangleDemo::TriangleDemo() : 
    solidColorVS_( 0), solidColorPS_( 0), inputLayout_( 0), vertexBuffer_( 0)
{


}

TriangleDemo::~TriangleDemo()
{

}

void TriangleDemo::UnloadContent()
{
    if( solidColorVS_)  solidColorVS_->Release();
    if( solidColorPS_)  solidColorPS_->Release();
    if( inputLayout_)     inputLayout_->Release();
    if( vertexBuffer_)    vertexBuffer_->Release();

    solidColorVS_ = 0;
    solidColorPS_ = 0;
    inputLayout_ = 0;
    vertexBuffer_ = 0;
}

正如前面我们说的:

从这第三章以来, 我们讨论过, 为了能够渲染几何体, 我们需要如下几点:

1. vertex buffer

2. vertext buffer 使用的描述 vertext layout 的 input layout

3.一系列的 shader

 

接下来是 加载资源的 LoadContent() 函数.
LoadContent() 首先加载 vertex shader 定义在 SolidGreenColor.fx 内, 这个文件名表明, 这个shader是 "给一个表面着上固定颜色".
接着, vertex shader 源文件被加载 并编译成功后, 调用 CreateVertexShader() 将生成实际有效的 shader, 这个shader 将会存放到内存, 因为 要和 vertex layout( 顶点布局)做签名验证.
再接着, 用相同的方式 创建 pixel shader.
下面是 创建 vertex shader的过程 (其中的 Dx11DemoBase::CompileD3DShader() 是新添加的基类函数, 因为一般应用需要使用 很多不同的shader , 而每个效果都要加载并编译生成shader, 该函数正式为了减少重复代码的编写):

bool TriangleDemo::LoadContent()
{
    ID3DBlob    *vsBuffer = 0;
    
    // 第一步, 加载 并最终生成 shader
    bool compileResult = CompileD3DShader( "SolidGreenColor.fx", "VS_Main", "vs_4_0", &vsBuffer);
    if( compileResult == false)
    {
        MessageBox( 0, "Error loading vertex shader !", "Compile Error", MB_OK);
        return false;
    }


    HRESULT d3dResult;
    d3dResult = d3dDevice_->CreateVertexShader( vsBuffer->GetBufferPointer(), vsBuffer->GetBufferSize(), 0, &solidColorVS_);
    if( FAILED( d3dResult))
    {
        if( vsBuffer)
            vsBuffer->Release();

        return false;
    }

    // 第二, 创建输入布局信息
    D3D11_INPUT_ELEMENT_DESC solidColorLayout[] = {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}
    };

    unsigned int totalLayoutElements = ARRAYSIZE( solidColorLayout);
    d3dResult = d3dDevice_->CreateInputLayout( solidColorLayout, totalLayoutElements, vsBuffer->GetBufferPointer(), vsBuffer->GetBufferSize(), &inputLayout_);
    vsBuffer->Release();

    if( FAILED( d3dResult))
    {
        return false;
    }


    // 下面是 pixel shader 部分
    ID3DBlob *psBuffer = 0;
    compileResult = CompileD3DShader( "SolidGreenColor.fx", "PS_Main", "ps_4_0", &psBuffer);
    if( compileResult == false)
    {
        MessageBox( 0, "Error loading pixel shader !", "Compile Error", MB_OK);
        return false;
    }

    d3dResult = d3dDevice_->CreatePixelShader( psBuffer->GetBufferPointer(), psBuffer->GetBufferSize(), 0, &solidColorPS_);
    psBuffer->Release();
    if( FAILED( d3dResult))
    {
        return false;
    }

    //...(未完)

 

下面是 Dx11DemoBase::CompileD3DShader() 实现部分:

bool Dx11DemoBase::CompileD3DShader( char *filePath, char *entry, char *shaderModel, ID3DBlob **buffer)
{
    DWORD shaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined( DEBUG) || defined( _DEBUG)
    shaderFlags |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob *errorBuffer = 0;
    HRESULT result;
    result = D3DX11CompileFromFile( filePath, 0, 0, entry, shaderModel, shadeFlags, 0, 0, buffer, &errorBuffer, 0);
    if( FAILED( result))
    {
        if( errorBuffer != 0 )
        {
            OutputDebugStringA( (char*)errorBuffer->GetBufferPointer());
            errorBuffer->Release();
        }    

        return false;
    }

    if( errorBuffer != 0)
        errorBuffer->Release();

    return true;
}

 

TriangleDemo::LoadContent() 的 上半部分完成了 inputlayout 和 shader, 剩下的就是 vertex 这些顶点的生成及处理. 我们demo 定义的三角形很简单: 在 X 和 Y 轴上各占一般的 空间(长度), Z轴( 即XY平面) 上的偏移量为 0.5f, 因为如果 摄像头 太靠近几何体所在 表面或者在 表明的背后, 这些 表面surface 都是不会被渲染的, 致使 摄像头看不到任何 我们预期的 面(或东西).

三角形的 顶点列表 vertex list 存放处在 vertices数组里, 同时就是这个 vertices 也作为 创建vertex buffer 函数 CreateBuffer() 的 参数之一:Subresource data. 

下面是 TriangleDemo::LoadContent() 的另一部分代码:

bool TriangleDemo::LoadContent()
{
    ...

    
    // 几何体信息
    VertexPos vertices[] = {
        XMFLOAT3( 0.5f,  0.5f, 0.5f),
        XMFLOAT3( 0.5f, -0.5f, 0.5f), 
        XMFLOAT3(-0.5f, -0.5f, 0.5f)
    };

    D3D11_BUFFER_DESC vertexDesc;
    ZeroMemory( &vertexDesc, sizeof( vertexDesc) );
    vertexDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexDesc.ByteWidth = sizeof( VertexPos) * 3;

    D3D11_SUBRESOURCE_DATA resourceData;
    ZeroMemory( &resourceData, sizeof( resourceData));
    resourceData.pSysMem = vertices;

    d3dResult = d3dDevice_->CreateBuffer( &vertexDesc, &resourceData, &vertexBuffer_);
    if( FAILED( d3dResult))
    {
        return false;
    }

    return true;
}

 

 

Triangledemo类的剩下代码 是 渲染 几何体 和 shaders . TriangleDemo::Render() 函数进行 渲染 几何体, 函数代码跟文章开头部分的示例几乎一模一样, 不过先要进行 当前Context有效性检测. 

由于三角形不移动, 我们没有必要进行 清除 渲染对象rendering targets, 但本书的后续篇章中需要. 接着是 input assembler 创建 并绑定, 提供将要被绘制 的 vertex buffer, 设置 三角形的绘制方式:

 

void TriangleDemo::Render()
{
    if( d3dContext_ == 0)
        return ;

    float clearColor[ 4] = { 0.0f, 0.0f, 0.25f, 1.0f };
    d3dContext_->ClearRenderTargetView( backBufferTarget_, clearColor);

    unsigned int stride = sizeof( VertexPos);
    unsigned int offset = 0;

    d3dContext_->IASetInputLayout( inputLayout_);
    d3dContext_->IASetVertexBuffers( 0, 1, &vertexBuffer_, &stride, &offset);
    d3dContext_->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    d3dContext_->VSSetShader( solidColorVS_, 0, 0);
    d3dContext_->PSSetShader( solidColorPS_, 0, 0);
    d3dContext_->Draw( 3, 0);

    swapChain_->Present( 0, 0);
}

 

如果说, 还有什么要交代的, 只能说是 shaders了( *.fx文件). demo 中的vertex shader 是相当简单的, 仅仅是 传递 输入的 顶点坐标vertex position 到输出设备; 后面我们才会 需要修改 船进入来的 参数数据 , 然后再正确地输出到 设备. pixel shader的工作也很简单, 仅仅是 为每个 像素pixel 配置一种固定的颜色( 绿色). pixel shader 定义颜色是采用 4个浮点数float 声明的,  分别代表 红,绿,蓝 和 alpha 四个颜色通道. 浮点数的 范围 是 0.0f 到 1.0f , 分别映射到用 unsigned char 表示的 0 到 255 范围; 即 0.0f 映射 0, 1.0f 映射 255   .

即是说, vertex shader( 就是一个带输入参数, 输出的函数) 的输出返回值, 就是 它的 输入参数值, 如此简单, 除非我们在之后 使用 geometry shader 绑定到 input assembler, 才不会这样"左耳进,右耳出".; pixel shader 的输出值 则是到 output buffer 固定颜色, 当调用 swapchain的 Present() 函数时, 这个output buffer 就会被输出给我们( 的显示器/或输出设备);从下面的的代码可以看出这两个 shader的 输入/输出:

// 输入一个顶点坐标, 类型为 POSITION
// 输出一个顶点坐标(已着色的), 类型为 SV_POSITION
float4 VS_Main( float4 pos : POSITION ) : SV_POSITION
{
    // 直接返回输入的参数值 pos
    return pos;
}


//输入一个 顶点坐标( 已着色的), 类型为 SV_POSITION
//输出一个 可直接显示在目标上的 颜色( 已着色的), 类型为 SV_TARGET
float4 PS_Main( float4 pos : SV_POSITION ) : SV_TARGET
{
    // 直接返回 固定的颜色, 输入值 pos 不做任何处理
    return float4( 0.0f, 1.0f, 0.0f, 1.0f );
}

//注意 , 上述的 ( 已着色的) 是指 shadered.

最终运行效果如图:

 

下面这是 源代码

转载于:https://www.cnblogs.com/Wilson-Loo/archive/2012/12/02/2797583.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值