Directx11基础教程三之VertexShader,PixelShader,buffer

一,看本节教程前应该掌握:
     (1)D3D11基础教程二之D3D11初始化
     (2)了解3D渲染流水线的知识,如世界变换,相机变换,透视投影变换,视口变换,光栅化,线性插值,gouraud着色等,最好具备一定的图形学基础,我推荐一本书<3D游戏编程大师技巧>,这本书完整的讲解了3D渲染流水线的方方面面。

二,本节教程的程序结构:


三,VertexShader(顶点着色器)和PixelShader(像素着色器)的作用:
在讲解VertexShader(顶点着色器)和PixelShader(像素着色器)的作用之前,我先放出一张D3D11简略版的3D渲染流水线图:


(1)VertexShader(顶点着色器):顶点着色器顾名思义,也就是对顶点进行操作,对顶点进行变换的着色器,看上面图红色圈住的部分就是顶点变换的过程了,而我们的VertexShader(顶点着色器),一般是对顶点进行上面红色圈的 世界变换(WorldTransform),   相机变换(也称视角变换,ViewTransform),    透视投影变换(PerspectiveProjection),(得注意的是"视口变换"不属于VertexShader,而是与"背面剔除'和"顶点属性线性插值"共属于光栅化阶段")剩下的几种变换经常是显卡帮我们自动进行的。

(2) PixelShader(像素着色器):像素着色器顾名思义,也就是对像素进行的操作的着色器,也就是上面图里的蓝色圈住的部分。像素着色器主要是对像素(像素的位置,颜色,向量等)操作,最终输出像素的颜色。

(3)VertexShader(顶点着色器)和PixelShader(像素着色器)之间的关系:这里用一个三角形作为例子,一个三角形由三个顶点ABC组成,三角形ABC位于局部空间,三个顶点ABC分别在VertexShader进行世界变换,相机变换等等各种变换,最终变换到屏幕空间,然后对屏幕空间的三角形ABC进行光栅化,得到三角形里面的像素,然后用PixelShader对像素进行操作,最终输出颜色到屏幕上。如下面图所示:

看图中的三角形ABC以及三角形ABC里面的像素,图中我用红色点表示像素,一个红色点就是一个像素。

(其实上面也不对,这里为了初学者教学的简便,我们就不引入其它种类的Shader了,以及深度缓存一大堆乱七八糟的)

三,程序的代码:

首先能GraphicsClass包含四个类,分别为D3DClass,ModelClass,CameraClass,ColorShaderClass

(1) GrapgicsClass

GraphicsClass.h

#pragma once
#ifndef _GRAPHICS_CLASS_H
#define _GRAPHICS_CLASS_H
 
#include"D3DClass.h"
#include"CameraClass.h"
#include"ColorShaderClass.h"
#include"ModelClass.h"
 
 
//全局变量
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLE = true;  //是尽可能快渲染还是限制帧渲染
const float SCREEN_FAR = 1000.0f;  //视截体远裁面
const float SCREEN_NEAR = 0.1f;  //视截体近裁面
 
 
 
class GraphicsClass
{
 
private:
    //D3D类
    D3DClass* mD3D;
 
    //相机类,用于控制场景的相机
    CameraClass* mCamera; 
 
    //用于控制VertexShader PixelShader,InputLayout
    ColorShaderClass* mColorShader;
 
    //用于控制VertexBuffer和IndexBuffer
    ModelClass* mModel;
 
private:
    bool Render();
 
public:
    GraphicsClass();
    GraphicsClass(const GraphicsClass&);
    ~GraphicsClass();
 
public:
    bool Initialize(int ScreenWidth, int ScreenHeight, HWND hwnd);
    void Shutdown();
    bool Frame(); 
};
#endif // !_GRAPHICS_CLASS_H


(2) ColorShaderClass

ColorShaderClass.h  (用于加载VertexShader和PixelShader)

#pragma once
#ifndef _COLOR_SHADER_CLASS_H
#define _COLOR_SHADER_CLASS_H
#define HR2(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}}
 
#include<d3d11.h>
#include<xnamath.h>
#include<D3DX11.h> //含编译Shader程序的函数
#include<d3dcompiler.h>
#include<fstream>
using namespace std;
 
class ColorShaderClass
{
 
private:
    //常量缓存结构体
    struct CBMatrix
    {
        XMMATRIX mWorldMatrix;
        XMMATRIX mViewMatrix;
        XMMATRIX mProjMatrix;
    };
 
private:
    ID3D11VertexShader* md3dVertexShader;
    ID3D11PixelShader* md3dPixelShader;
    ID3D11InputLayout* md3dInputLayout; //这与VertexShader相关联,因此要放在ColorShaderClass里,而不是D3DClass
    ID3D11Buffer* mCBMatrixBuffer; //(常量)缓存,顶点索引也是用这个类型
 
private:
    bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); //用于创建InputLayout,VertexShader,PixelShader,常量缓存
    bool ShutdownShader();
    void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
 
    bool SetShaderParameter(ID3D11DeviceContext*, CXMMATRIX, CXMMATRIX, CXMMATRIX);
    void RenderShader(ID3D11DeviceContext*, int);
 
public:
    ColorShaderClass();
    ColorShaderClass(const ColorShaderClass&);
    ~ColorShaderClass();
 
public:
    bool Initialize(ID3D11Device*, HWND);
    void Shutdown();
    bool Render(ID3D11DeviceContext*, int, CXMMATRIX, CXMMATRIX, CXMMATRIX);
 
};
#endif 


ColorShaderClass.CPP
#include"ColorShaderClass.h"
 
ColorShaderClass::ColorShaderClass()
{
     md3dVertexShader=NULL;
     md3dPixelShader=NULL;
     md3dInputLayout=NULL;
     mCBMatrixBuffer=NULL;
}
 
 
ColorShaderClass::ColorShaderClass(const ColorShaderClass&)
{
 
}
 
ColorShaderClass::~ColorShaderClass()
{
 
}
 
 
bool ColorShaderClass::Initialize(ID3D11Device* d3dDevice, HWND hwnd)
{
    bool result;
    result = InitializeShader(d3dDevice, hwnd, L"MyShader.fx", L"MyShader.fx");
    if (!result)
        return false;
 
    return true;
}
 
void ColorShaderClass::Shutdown()
{
    ShutdownShader();
}
 
bool ColorShaderClass::Render(ID3D11DeviceContext* d3dDeviceContext, int indexCount, CXMMATRIX worldMatrix, CXMMATRIX viewMatrix, CXMMATRIX ProjMatrix)
{
    bool result;
 
    //设置用来渲染的Shader属性
    result = SetShaderParameter(d3dDeviceContext, worldMatrix, viewMatrix, ProjMatrix);
    if (!result)
        return false;
 
    //渲染Shader
    RenderShader(d3dDeviceContext, indexCount);
    
    return true;
}
 
bool ColorShaderClass::InitializeShader(ID3D11Device* d3dDevice, HWND hwnd, WCHAR* VSFileName, WCHAR* PSFileName)
{
    HRESULT result;
    ID3D10Blob* errorMessage;
    ID3D10Blob* VertexShaderBuffer;  
    ID3D10Blob* PixelShaderBuffer;
    
    //第一,初始化参数
    errorMessage = NULL;
    VertexShaderBuffer=NULL;
    PixelShaderBuffer=NULL;
 
    //第二,编译VertexShader代码,并创建VertexShader
    result = D3DX11CompileFromFile(VSFileName, NULL, NULL, "VS", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &VertexShaderBuffer, &errorMessage, NULL);
    if (FAILED(result))
    {
        //存在错误信息
        if (errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, VSFileName);
        }
        //不存在错误信息,也就是没有找到Shader文件
        else
        {
            MessageBox(hwnd, L"can not find VS file", L"error", MB_OK);
        }
    }
 
    HR2(d3dDevice->CreateVertexShader(VertexShaderBuffer->GetBufferPointer(),VertexShaderBuffer->GetBufferSize(),NULL,&md3dVertexShader));
 
 
    //第三,编译PixelShader,并创建PixelShader
    result = D3DX11CompileFromFile(PSFileName, NULL, NULL, "PS", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &PixelShaderBuffer, &errorMessage, NULL);
    if (FAILED(result))
    {
        //存在错误信息
        if (errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, PSFileName);
        }
        //不存在错误信息,也就是没有找到Shader文件
        else
        {
            MessageBox(hwnd, L"can not find PS file", L"error", MB_OK);
        }
    }
 
    HR2(d3dDevice->CreatePixelShader(PixelShaderBuffer->GetBufferPointer(), PixelShaderBuffer->GetBufferSize(), NULL, &md3dPixelShader));
 
    //第四,填充输入布局形容结构体,创建输入布局
    D3D11_INPUT_ELEMENT_DESC VertexInputLayout[] =
    {
        { "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0 }, // 96位即12个字节
        { "COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0 },
    };
 
    unsigned int numElements = sizeof(VertexInputLayout) / sizeof(VertexInputLayout[0]);         //布局数量
    
    HR2(d3dDevice->CreateInputLayout(VertexInputLayout, numElements, VertexShaderBuffer->GetBufferPointer(), VertexShaderBuffer->GetBufferSize(), &md3dInputLayout));
 
    //第五,释放VertexShaderBuffer和PixelShaderBuffer
    VertexShaderBuffer->Release();
    VertexShaderBuffer = NULL;
    PixelShaderBuffer->Release();
    PixelShaderBuffer = NULL;
 
    //第六,设置(常量)缓存形容结构体,并创建常量缓存
    D3D11_BUFFER_DESC matrixBufferDesc;
    ZeroMemory(&matrixBufferDesc, sizeof(matrixBufferDesc));
    matrixBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    matrixBufferDesc.ByteWidth = sizeof(CBMatrix);   //结构体大小,必须为16字节倍数
    matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    matrixBufferDesc.CPUAccessFlags = 0; 
 
    HR2(d3dDevice->CreateBuffer(&matrixBufferDesc, NULL, &mCBMatrixBuffer));
 
    return true;
}
 
bool ColorShaderClass::ShutdownShader()
{
    HR2(mCBMatrixBuffer);
    HR2(md3dInputLayout);
    HR2(md3dPixelShader);
    HR2(md3dVertexShader);
    return true;
}
 
void ColorShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename)
{
    char* compileErrors;
    unsigned long bufferSize, i;
    ofstream fout;
 
 
    // 获取指向错误信息文本的指针
    compileErrors = (char*)(errorMessage->GetBufferPointer());
 
    // 获取错误信息文本的长度
    bufferSize = errorMessage->GetBufferSize();
 
    // 创建一个txt,用于写入错误信息
    fout.open("shader-error.txt");
 
    //想txt文件写入错误信息
    for (i = 0; i<bufferSize; i++)
    {
        fout << compileErrors[i];
    }
 
    // 关闭文件
    fout.close();
 
    // Release the error message.
    errorMessage->Release();
    errorMessage = 0;
 
    //弹出提醒的小窗口
    MessageBox(hwnd, L"Error compiling shader.  Check shader-error.txt for message.", shaderFilename, MB_OK);
 
}
 
 
bool ColorShaderClass::SetShaderParameter(ID3D11DeviceContext* d3dDeviceContext, CXMMATRIX worldMatrix, CXMMATRIX viewMatrix, CXMMATRIX ProjMatrix)
{
    //D3D11_MAPPED_SUBRESOURCE mappedResource;
    
 
    //CBMatrix* cbPtr;
    unsigned int bufferNum;
 
    //将矩阵转置,在传入常量缓存前进行转置,因为GPU对矩阵数据会自动进行一次转置
    CBMatrix cb;
    XMMATRIX worldMa = XMMatrixTranspose(worldMatrix);
    XMMATRIX viewMa = XMMatrixTranspose(viewMatrix);
    XMMATRIX ProjMa = XMMatrixTranspose(ProjMatrix);
    cb.mWorldMatrix = worldMa;
    cb.mViewMatrix = viewMa;
    cb.mProjMatrix = ProjMa;
    d3dDeviceContext->UpdateSubresource(mCBMatrixBuffer, 0, NULL, &cb, 0, 0);
    /**/
    //锁定常量缓存,这时候常量缓存和子资源关联在一起
    //HR2(d3dDeviceContext->Map(mCBMatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
 
    //获取指向常量缓存数据的指针
    //cbPtr = (CBMatrix*)mappedResource.pData;
 
    //赋予常量缓存数据
    //cbPtr->mProjMatrix = worldMa;
    //cbPtr->mViewMatrix = viewMa;
    //cbPtr->mProjMatrix = ProjMa;
 
    //解锁常量缓存
    //d3dDeviceContext->Unmap(mCBMatrixBuffer, 0);
    
    //设置在顶点缓存中常量缓存的位置,注册号
    bufferNum = 0;
 
    //设置在VertexShader的常量缓存的值(带着更新的值)
    d3dDeviceContext->VSSetConstantBuffers(bufferNum, 1, &mCBMatrixBuffer); 
 
    return true;
}
 
void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
    //设置顶点输入布局
    deviceContext->IASetInputLayout(md3dInputLayout);
 
    //设置VertexShader和PixelShader
    deviceContext->VSSetShader(md3dVertexShader, NULL, 0);
    deviceContext->PSSetShader(md3dPixelShader, NULL, 0);
 
    //渲染三角形
    deviceContext->DrawIndexed(indexCount, 0, 0);
}

(3) ModelClass


ModelClass.h(用于加载顶点缓存和索引缓存)

#pragma once
#ifndef _MODEL_CLASS_H
#define _MODEL_CLASS_H
 
#include<d3d11.h>
#include<xnamath.h>
#define HR1(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}}
#define ReleaseCOM1(x) { if (x) { x->Release(); x = 0; } }
 
class ModelClass
{
private:
    struct Vertex
    {
        XMFLOAT3 pos;
        XMFLOAT4 color;
    };
private:
    ID3D11Buffer* md3dVertexBuffer; //顶点缓存
    ID3D11Buffer* md3dIndexBuffer;  //索引缓存
    int mVertexCount;
    int mIndexCount;
 
private:
    bool InitializeBuffer(ID3D11Device* d3dDevice);
    void ShutdownBuffer();
    void RenderBuffers(ID3D11DeviceContext* d3dDeviceContext);
 
public:
    ModelClass();
    ModelClass(const ModelClass&);
    ~ModelClass();
 
public:
    //Initialize是创建元素,Render是设置元素,Shutdown是Release
    bool Initialize(ID3D11Device* d3dDevice);
    void Shutdown();
    void Render(ID3D11DeviceContext* d3dDeviceContext);
 
    int GetIndexCount() { return mIndexCount; }
 
};
#endif 


ModelClass.CPP
#include"ModelClass.h"
 
ModelClass::ModelClass()
{
    md3dVertexBuffer=NULL; //顶点缓存
    md3dIndexBuffer=NULL;  //索引缓存
    mVertexCount = 0;
    mIndexCount = 0;
 
}
 
 
ModelClass::ModelClass(const ModelClass& other)
{
 
}
 
ModelClass::~ModelClass()
{
 
}
 
bool ModelClass::Initialize(ID3D11Device* d3dDevice)
{
    bool result;
    result = InitializeBuffer(d3dDevice);
 
    if (!result)
        return false;
 
        return true;
}
 
void ModelClass::Shutdown()
{
    ShutdownBuffer();
}
 
 
void ModelClass::Render(ID3D11DeviceContext* d3dDeviceContext)
{
    //设置渲染管线的顶点缓存和索引缓存(IA阶段)
    RenderBuffers(d3dDeviceContext);
}
 
bool ModelClass::InitializeBuffer(ID3D11Device* d3dDevice)
{
    Vertex* vertexs=NULL;
    WORD*indices=NULL;  //一个字为两个字节 
 
    mVertexCount = 4;
    mIndexCount = 6;
 
    //创建顶点数组
    vertexs = new Vertex[mVertexCount];
    if (!vertexs)
        return false;
 
    //创建索引数组
    indices = new WORD[mIndexCount];
    if (!indices)
        return false;
    
    //赋予顶点数组数据和索引数组数据
    vertexs[0].pos = XMFLOAT3(-1.0f, -1.0f, 0.0f);
    vertexs[0].color = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);
    vertexs[1].pos = XMFLOAT3(1.0f, 1.0f, 0.0f);
    vertexs[1].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
    vertexs[2].pos = XMFLOAT3(1.0f, -1.0f, 0.0f);
    vertexs[2].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
    vertexs[3].pos = XMFLOAT3(-1.0f, 1.0f, 0.0f);
    vertexs[3].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
    //赋予索引数组数据
 
    //注意用左手定则判定是不是背面
    indices[0] = 0;
    indices[1] = 3;
    indices[2] = 2;
    indices[3] = 1;
    indices[4] = 2;
    indices[5] = 3;
 
 
    //第一,填充(顶点)缓存形容结构体和子资源数据结构体,并创建顶点缓存
    D3D11_BUFFER_DESC vertexBufferDesc;
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(Vertex) * mVertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;
 
    D3D11_SUBRESOURCE_DATA vertexData;
    vertexData.pSysMem = vertexs;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;
    HR1(d3dDevice->CreateBuffer(&vertexBufferDesc, &vertexData, &md3dVertexBuffer));
 
    //第二,填充(索引)缓存形容结构体和子资源数据结构体,并创建索引缓存
    D3D11_BUFFER_DESC  indexBufferDesc;
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(WORD) * mIndexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;
 
    D3D11_SUBRESOURCE_DATA indexData;
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;
    HR1(d3dDevice->CreateBuffer(&indexBufferDesc, &indexData, &md3dIndexBuffer));
 
    //释放顶点数组和索引数组(这时数据已经载入缓存,不需要这些数组了)
    delete[]vertexs;
    vertexs = NULL;
    delete[]indices;
    indices = NULL;
    
    return true;
}
 
void ModelClass::ShutdownBuffer()
{
    //释放顶点缓存和索引缓存
    ReleaseCOM1(md3dIndexBuffer);
    ReleaseCOM1(md3dVertexBuffer);
}
 
void ModelClass::RenderBuffers(ID3D11DeviceContext* d3dDeviceContext)
{
    //设置顶点缓存
    UINT stride = sizeof(Vertex); //每个顶点元素的跨度大小,或者说每个顶点元素的大小
    UINT offset = 0;
    d3dDeviceContext->IASetVertexBuffers(0, 1, &md3dVertexBuffer, &stride, &offset);
 
    //设置索引缓存
    d3dDeviceContext->IASetIndexBuffer(md3dIndexBuffer, DXGI_FORMAT_R16_UINT, 0); //Word为两个字节
 
    //设置拓扑方式
    d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}


四,本节教程的程序得注意的地方:
(1)在一个.h文件里包含XNAMATH.h一定得先包含一个windows.h。

(2)XMMATRIX作为函数参数时应该用CXMMATRIX。

(3)指定顶点的索引值时得注意绕线方向,用左手定则,得到三角形法线方向跟相机LookAt方向为钝角的被正面,反之为背面.

(4)我的VertexShader和PixelShader都在从MyShader.fx文件中编译的,其实.HLSL文件.VS文件.PS文件.FX文件都能编译成Shader

(5)在.FX文件中常量缓存CBMatrix注意用register()注册,注册为b0,则在ColorShaderClass::SetShaderParameter()的d3dDeviceContext->VSSetConstantBuffers(bufferNum, 1, &mCBMatrixBuffer)的bufferNum为0,若注册为b1,则bufferNum为1,以此类推.

(6)在VertexShader阶段和PixelShade阶段,常量缓存的值一直未曾改变,不会进行光栅化,因此世界空间的平行光,聚光灯,点光源都是采用常量缓存。

(7)在对应与常量缓存的结构体一定是16个字节的倍数,XMMATRIX为64个字节,float为4个字节,实在要是怕忘记16字节对齐规则,可在结构体前加个前缀,__declspec(align(16))  ,比如

    __declspec(align(16))  
    struct CbNeverChange   //常量缓存结构严格16字节的倍数
    {
        DirectionLight DireLight;
        PointLight PoLight;
        SpotLight SpLight;
        Material SkullMaterial;
        XMFLOAT3 EyePostion;
    };

五,本节教程的程序运行的结果:


六,程序源代码链接如下
点击打开链接
--------------------- 
作者:小毛狗 
来源:优快云 
原文:https://blog.youkuaiyun.com/qq_29523119/article/details/52716762 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值