前言

由于在Direct3D 11中取消了固定管线,要想绘制图形必须要了解可编程渲染管线的流程,一个能绘制出图形的渲染管线最少需要有这两个可编程着色器:顶点着色器像素着色器

在阅读本章内容之前,你还需要先浏览下面的章节

章节
HLSL编译着色器的三种方法

学习目标:

  1. 初步了解渲染管线
  2. 能够动手编写第一个顶点着色器和像素着色器
  3. 掌握HLSL编译着色器的方法
  4. 掌握将着色器绑定到渲染管线上的代码实现
  5. 了解顶点缓冲区,掌握创建和绑定到输入装配阶段的方法
  6. 渲染出第一个三角形

DirectX11 With Windows SDK完整目录

Github项目源码

欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。

渲染管线概述(渲染流水线)

若给出某个3D场景的几何描述,并在其中架设一台具有确定位置和朝向的虚拟摄像机,那么渲染管线(rendering pipeline)是以此摄像机为观察视角而生成2D图像的一系列完整步骤。下图展示了DirectX11的渲染管线完整流程:

其中椭圆区域的着色器阶段是可编程的,而其余阶段只提供了有限的设置。默认情况下当前阶段的着色器为null时,可以跳过当前步骤。对于初学者来说最常用的是下面这种情况:

输入装配阶段

输入装配阶段会从显存中读取顶点和索引(如果有)数据,再将它们装配成几何图元。通常用到的图元拓扑有点、线段和三角形。

顶点着色器阶段

待图元被装配完毕后,其顶点就会被送入到顶点着色器阶段。我们可以把顶点着色器看作一种输入与输出皆为单个顶点的函数,只是输入的顶点和输出的顶点不仅内容上可能有变化,顶点包含的属性也可能有变化。

我们可以利用顶点着色器来实现许多特效,例如变换、光照和置换贴图。在顶点着色器中,不但可以访问输入的顶点数据,也能够访问纹理和其他存于显存中的数据(如变换矩阵与场景的光照信息)。

光栅化阶段

光栅化阶段需要完成的任务很多,其中包括对输出的顶点进行视口变换,这样x、y坐标都将以像素为单位表示。然后根据原顶点和图元,确定当前图元会出现在待绘制纹理的哪些像素位置上,根据图元的顶点信息,并利用透视校正插值法来计算出像素片段(pixel fragment)的位置、颜色(如果有)等信息。

对于三角形来说,不同的顶点顺序有不同的含义。默认情况下,三角形的顶点绕顺时针排布时(从摄像机朝着该三角形看)则说明当前三角形的面朝向当前摄像机,而三角形的顶点绕逆时针排布则说明当前三角形的面背对当前摄像机。对于背朝摄像机的三角形会被剔除,从而不进行光栅化。这种做法叫背面消隐

在光栅化的时候,每个像素都是以它的中点位置进行插值计算的。如下图所示,若只有2x2的像素,黑色顶点为构成三角形的顶点,蓝色顶点则为光栅化时选择的顶点以及插值计算得到的结果。

像素着色器阶段

作为可编程着色器阶段,它会针对每一个像素片段进行处理(每处理一个像素就要执行一次像素着色器)。它既可以直接返回一种单一的恒定颜色,也可以实现如逐像素光照、反射及阴影等更为复杂的效果

输出合并阶段

通过像素着色器生成的像素片段会被移送至渲染管线的输出合并阶段。在这个阶段中,一些像素片段需要经过模板测试深度测试来确定是否能留下,然后必要的时候还需要与绑定的缓冲区对应像素片段进行混合操作,可以实现透明等效果。

只有当我们接触到后续的管线阶段时,才会进行更加详细的展开叙述。

第一份HLSL代码

现在我们在项目中创建HLSL文件夹,将所有的着色器代码放到这里。

在里面创建一个Triangle.hlsli的文件,内容如下:

// Triangle.hlsli

struct VertexIn
{
float3 pos : POSITION;
float4 color : COLOR;
}; struct VertexOut
{
float4 posH : SV_POSITION;
float4 color : COLOR;
};

接下来创建Triangle_VS.hlsl文件,用于存放顶点着色器代码:

#include "Triangle.hlsli"

// 顶点着色器
VertexOut VS(VertexIn vIn)
{
VertexOut vOut;
vOut.posH = float4(vIn.pos, 1.0f);
vOut.color = vIn.color; // 这里alpha通道的值默认为1.0
return vOut;
}

最后创建Triangle_PS.hlsl文件,用于存放像素着色器代码:

#include "Triangle.hlsli"

// 像素着色器
float4 PS(VertexOut pIn) : SV_Target
{
return pIn.color;
}

HLSL代码的语法和C/C++的语法非常相似,也许后面会开坑描述一下HLSL语言,不过现在先把注意力放在这份代码中比较特别的地方。

float3float4都是内置的变量类型,可以看作是C++的struct类型,支持多种构造方式和成员访问。除此之外,还有floatfloat2两种类型。对于float4,它的四个成员分别为x,y,zw

然后具体讲述一下变量名后面的语义:

语义名 具体含义
POSITION 描述该变量是一个坐标点
SV_POSITION 说明该顶点的位置在从顶点着色器输出后,后续的着色器都不能改变它的值,作为光栅化时最终确定的像素位置
COLOR 描述该变量是一个颜色
SV_Target 说明输出的颜色值将会直接保存到渲染目标视图的后备缓冲区对应位置

输入布局(Input Layout)

ID3D11Device::CreateInputLayout方法--创建输入布局

在HLSL中,用于输入的结构体为:

struct VertexIn
{
float3 pos : POSITION;
float4 color : COLOR;
};

该项目与之对应的C++结构体为:

struct VertexPosColor
{
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT4 color;
static const D3D11_INPUT_ELEMENT_DESC inputLayout[2];
};

注意:DX SDK中的xnamath.h在Windows SDK中已经被抛弃,取而代之的则是要包含头文件directxmath.h,XNA相关的数学库基本上都移植到这里了,除此之外,他们都已经被放入到名称空间DirectX中。

由于顶点缓冲区的本质是二进制流,为了能够建立C++结构体与HLSL结构体的对应关系,需要使用ID3D11InputLayout输入布局来描述每一个成员的用途、语义、大小等信息。

还要留意的是,其中inputLayout并不是结构体VertexPosColor的内部成员,而是静态成员,不占用该结构体的空间。我们使用D3D11_INPUT_ELEMENT_DESC结构体来描述待传入结构体中每个成员的具体信息,定义如下:

typedef struct D3D11_INPUT_ELEMENT_DESC
{
LPCSTR SemanticName; // 语义名
UINT SemanticIndex; // 语义索引
DXGI_FORMAT Format; // 数据格式
UINT InputSlot; // 输入槽索引(0-15)
UINT AlignedByteOffset; // 初始位置(字节偏移量)
D3D11_INPUT_CLASSIFICATION InputSlotClass; // 输入类型
UINT InstanceDataStepRate; // 忽略
} D3D11_INPUT_ELEMENT_DESC;

inputLayout的初始化信息如下,描述了C++对应到HLSL的两个成员的信息:

const D3D11_INPUT_ELEMENT_DESC VertexPosColor::inputLayout[2] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};

其中,语义名要与HLSL结构体中的语义名相同,若有多个相同的语义名,则语义索引就是另外一种区分。相同的语义按从上到下所以分别为0,1,2...

然后,DXGI_FORMAT在这里通常描述数据的存储方式、大小。用DXGI_FORMAT_R32G32B32_FLOAT仅仅是解释为3个float类型的值;而用DXGI_FORMAT_R32G32B32A32_FLOAT在这里是说明颜色按RGBA存储,并且为4个float类型的值

输入槽这里只使用1个,即索引为0的输入槽。

初始位置则指的是该成员的位置与起始成员所在的字节偏移量。

输入类型有两种:D3D11_INPUT_PER_VERTEX_DATA为按每个顶点数据输入,D3D11_INPUT_PER_INSTANCE_DATA则是按每个实例数据输入。

通过语义、数据类型和起始偏移量,我们就可以建立起C++顶点缓冲区数据和HLSL顶点之间的联系。

接下来就可以使用ID3D11Device::CreateInputLayout方法创建一个输入布局:

HRESULT ID3D11Device::CreateInputLayout(
const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs, // [In]输入布局描述
UINT NumElements, // [In]上述数组元素个数
const void *pShaderBytecodeWithInputSignature, // [In]顶点着色器字节码
SIZE_T BytecodeLength, // [In]顶点着色器字节码长度
ID3D11InputLayout **ppInputLayout); // [Out]获取的输入布局

ID3D11DeviceContext::IASetInputLayout方法--输入装配阶段设置输入布局

下面的方法可以让我们使用刚创建好的输入布局:

void ID3D11DeviceContext::IASetInputLayout(
ID3D11InputLayout *pInputLayout); // [In]输入布局

顶点/像素着色器的创建

ID3D11Device::CraeteXXXXShader方法--创建着色器

从D3D设备可以创建出6种着色器:

方法 着色器类型 描述
ID3D11Device::CreateVertexShader ID3D11VertexShader 顶点着色器
ID3D11Device::CreateHullShader ID3D11HullShader 外壳着色器
ID3D11Device::CreateDomainShader ID3D11DomainShader 域着色器
ID3D11Device::CreateComputeShader ID3D11ComputeShader 计算着色器
ID3D11Device::CreateGeometryShader ID3D11GeometryShader 几何着色器
ID3D11Device::CreatePixelShader ID3D11PixelShader 像素着色器

这些方法的输入形参都是一致的,只是输出的是不同的着色器,以创建顶点着色器的方法为例:

HRESULT ID3D11Device::CreateVertexShader(
const void *pShaderBytecode, // [In]着色器字节码
SIZE_T BytecodeLength, // [In]字节码长度
ID3D11ClassLinkage *pClassLinkage, // [In_Opt]忽略
ID3D11VertexShader **ppVertexShader); // [Out]获取顶点着色器

GameApp::InitEffect方法--着色器或特效相关的初始化

下面展示了GameApp::InitEffect方法的实现,其中输入布局的创建也需要放到这里。

CreateShaderFromFile函数请到文章开头的 HLSL编译着色器的三种方法 查看。

bool GameApp::InitEffect()
{
ComPtr<ID3DBlob> blob; // 创建顶点着色器
HR(CreateShaderFromFile(L"HLSL\\Triangle_VS.cso", L"HLSL\\Triangle_VS.hlsl", "VS", "vs_5_0", blob.ReleaseAndGetAddressOf()));
HR(m_pd3dDevice->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pVertexShader.GetAddressOf()));
// 创建并绑定顶点布局
HR(m_pd3dDevice->CreateInputLayout(VertexPosColor::inputLayout, ARRAYSIZE(VertexPosColor::inputLayout),
blob->GetBufferPointer(), blob->GetBufferSize(), m_pVertexLayout.GetAddressOf())); // 创建像素着色器
HR(CreateShaderFromFile(L"HLSL\\Triangle_PS.cso", L"HLSL\\Triangle_PS.hlsl", "PS", "ps_5_0", blob.ReleaseAndGetAddressOf()));
HR(m_pd3dDevice->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pPixelShader.GetAddressOf())); return true;
}

GameApp::InitResource方法

顶点缓冲区(Vertex Buffer)

顶点缓冲区的作用是,将顶点数组以缓冲区ID3D11Buffer的形式提供给输入装配阶段。

ID3D11Device::CreateBuffer方法--创建一个缓冲区

要创建顶点缓冲区,首先需要填充好缓冲区描述D3D11_BUFFER_DESC

typedef struct D3D11_BUFFER_DESC
{
UINT ByteWidth; // 数据字节数
D3D11_USAGE Usage; // CPU和GPU的读写权限相关
UINT BindFlags; // 缓冲区类型的标志
UINT CPUAccessFlags; // CPU读写权限的指定
UINT MiscFlags; // 忽略
UINT StructureByteStride; // 忽略
} D3D11_BUFFER_DESC;

在这里需要详细讲述一下D3D11_USAGE枚举类型对应的读写关系:

CPU读 CPU写 GPU读 GPU写
D3D11_USAGE_DEFAULT
D3D11_USAGE_IMMUTABLE
D3D11_USAGE_DYNAMIC
D3D11_USAGE_STAGING

对于D3D11_USAGE_DEFAULT类型的缓冲区,应当使用 ID3D11DeviceContext::UpdateSubresource方法来更新缓冲区资源,它的原理是将内存中的某段数据传递到显存中,然后再将该显存中的数据复制到在显存中的缓冲区。这种更新方式我们是无法直接访问缓冲区的内容的。在绘制完成/开始前调用可以比较快地更新显存中的数据。

而对于D3D11_USAGE_DYNAMIC类型的缓冲区,则应当使用ID3D11DeviceContext::MapID3D11DeviceContext::Unmap方法,将显存中的数据映射到内存中,然后修改该片内存的数据,最后将修改好的数据映射回显存中。这种更新方式我们是可以直接获取来自显存的数据的,但代价就是更新的效率会比上面的方式更低一些。

由于目前的教程所涉及到的顶点缓冲区在创建后通常是不会修改的,因此将其设为D3D11_USAGE_IMMUTABLE

这里将创建包含三个顶点数据的缓冲区:

// 设置三角形顶点
// 注意三个顶点的给出顺序应当按顺时针排布
VertexPosColor vertices[] =
{
{ XMFLOAT3(0.0f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }
};
// 设置顶点缓冲区描述
D3D11_BUFFER_DESC vbd;
ZeroMemory(&vbd, sizeof(vbd));
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof vertices;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;

有了缓冲区描述,还需要使用D3D11_SUBRESOURCE_DATA结构体来指定要用来初始化的数据:

typedef struct D3D11_SUBRESOURCE_DATA
{
const void *pSysMem; // 用于初始化的数据
UINT SysMemPitch; // 忽略
UINT SysMemSlicePitch; // 忽略
} D3D11_SUBRESOURCE_DATA;

子资源数据结构体的填充也很简单:

// 新建顶点缓冲区
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = vertices;

最后通过ID3D11Device::CreateBuffer来创建一个顶点缓冲区:

HRESULT ID3D11Device::CreateBuffer(
const D3D11_BUFFER_DESC *pDesc, // [In]顶点缓冲区描述
const D3D11_SUBRESOURCE_DATA *pInitialData, // [In]子资源数据
ID3D11Buffer **ppBuffer); // [Out] 获取缓冲区

演示如下:

ComPtr<ID3D11Buffer> m_pVertexBuffer = nullptr;
HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.GetAddressOf()));

ID3D11DeviceContext::IASetVertexBuffers方法--渲染管线输入装配阶段设置顶点缓冲区

创建好顶点缓冲区后,就可以在渲染管线输入装配阶段设置该顶点缓冲区了:

void ID3D11DeviceContext::IASetVertexBuffers(
UINT StartSlot, // [In]输入槽索引
UINT NumBuffers, // [In]缓冲区数目
ID3D11Buffer *const *ppVertexBuffers, // [In]指向缓冲区数组的指针
const UINT *pStrides, // [In]一个数组,规定了对所有缓冲区每次读取的字节数分别是多少
const UINT *pOffsets); // [In]一个数组,规定了对所有缓冲区的初始字节偏移量
// 输入装配阶段的顶点缓冲区设置
UINT stride = sizeof(VertexPosColor); // 跨越字节数
UINT offset = 0; // 起始偏移量 m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset);

只要绘制的内容不变,该部分的设置则只需要进行一次即可,因为渲染管线中各个部分的设置方法一经调用就会立即生效。然而如果需要绘制不同的内容或者效果,则需要在绘制前给渲染管线绑定好各种所需的资源。

图元类型

D3D_PRIMITIVE_TOPOLOGY枚举定义了许多种图元类型,通常会根据顶点缓冲区的顶点索引(如果有索引缓冲区则是根据这些索引的值)和装配方式进行解释,其中:

图元类型 含义
D3D11_PRIMITIVE_TOPOLOGY_POINTLIST 按一系列点进行装配
D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP 按一系列线段进行装配,每相邻两个顶点(或索引数组相邻的两个索引对应的顶点)构成一条线段
D3D11_PRIMITIVE_TOPOLOGY_LINELIST 按一系列线段进行装配,每两个顶点(或索引数组每两个索引对应的顶点)构成一条线段
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP 按一系列三角形进行装配,每相邻三个顶点(或索引数组相邻的三个索引对应的顶点)构成一个三角形
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST 按一系列三角形进行装配,每三个顶点(或索引数组每三个索引对应的顶点)构成一个三角形
D3D11_PRIMITIVE_TOPOLOGY_LINELIST_ADJ 每4个顶点为一组,只绘制第2个顶点与第3个顶点的连线(或索引数组每4个索引为一组,只绘制索引模4余数为2和3的连线)
D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ 绘制除了最开始和结尾的所有线段(或者索引数组不绘制索引0和1的连线,以及n-2和n-1的连线)
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ 每6个顶点为一组,只绘制第1、3、5个顶点构成的三角形(或索引数组每6个索引为一组,只绘制索引模6余数为0, 2, 4的三角形)
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ 抛弃所有索引模2为奇数的顶点或索引,剩余的进行Triangle Strip的绘制

Point List

Line list(左) or line strip(右)

Triangle list(左) or triangle strip(右)

Line list with adjacency(左) or line strip with adjacency(右)

Triangle list with adjacency(v6, v8, v10也构成一个三角形)

Triangle strip with adjacency缺图

通常绝大多数情况下,我们都会使用D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST

ID3D11DeviceContext::IASetPrimitiveTopology方法--渲染管线输入装配阶段设置图元类型

void ID3D11DeviceContext::IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY Topology); // [In]图元类型

该操作只需要设置一次即可。

ID3D11DeviceContext::*SSetShader方法--给渲染管线某一着色阶段设置对应的着色器

这里的*可以是V, H, D, C, G, P,对应六个可编程渲染管线阶段,除了第一个参数提供的着色器类型不同外,其余参数一致。

ID3D11DeviceContext::VSSetShader为例:

void ID3D11DeviceContext::VSSetShader(
ID3D11VertexShader *pVertexShader, // [In]顶点着色器
ID3D11ClassInstance *const *ppClassInstances, // [In_Opt]忽略
UINT NumClassInstances); // [In]忽略

注意: 类似给渲染管线绑定资源的一切方法,在绑定之后就会一直生效,而不是说仅能够使用一次。所以,以后如果你需要用别的特效去绘制当前物体,就要重新绑定好渲染管线所需要的一切资源。

最后给出GameApp::InitResource方法的实现

bool GameApp::InitResource()
{
// 设置三角形顶点
VertexPosColor vertices[] =
{
{ XMFLOAT3(0.0f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }
};
// 设置顶点缓冲区描述
D3D11_BUFFER_DESC vbd;
ZeroMemory(&vbd, sizeof(vbd));
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof vertices;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
// 新建顶点缓冲区
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = vertices;
HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.GetAddressOf())); // ******************
// 给渲染管线各个阶段绑定好所需资源 // 输入装配阶段的顶点缓冲区设置
UINT stride = sizeof(VertexPosColor); // 跨越字节数
UINT offset = 0; // 起始偏移量 m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset);
// 设置图元类型,设定输入布局
m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_pd3dImmediateContext->IASetInputLayout(m_pVertexLayout.Get());
// 将着色器绑定到渲染管线
m_pd3dImmediateContext->VSSetShader(m_pVertexShader.Get(), nullptr, 0);
m_pd3dImmediateContext->PSSetShader(m_pPixelShader.Get(), nullptr, 0); return true;
}

GameApp::DrawScene方法

ID3D11DeviceContext::Draw方法--根据已经绑定的顶点缓冲区进行绘制

该方法不需要提供索引缓冲区:

void ID3D11DeviceContext::Draw(
UINT VertexCount, // [In]需要绘制的顶点数目
UINT StartVertexLocation); // [In]起始顶点索引

调用该方法后,从输入装配阶段开始,该绘制的进行将会经历一次完整的渲染管线阶段,直到输出合并阶段为止。

通过指定VertexCountStartVertexLocation的值我们可以按顺序绘制从索引StartVertexLocationStartVertexLocation + VertexCount - 1的顶点

GameApp::DrawScene方法的实现如下:

void GameApp::DrawScene()
{
assert(m_pd3dImmediateContext);
assert(m_pSwapChain); static float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // RGBA = (0,0,0,255)
m_pd3dImmediateContext->ClearRenderTargetView(m_pRenderTargetView.Get(), black);
m_pd3dImmediateContext->ClearDepthStencilView(m_pDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); // 绘制三角形
m_pd3dImmediateContext->Draw(3, 0);
HR(m_pSwapChain->Present(0, 0));
}

最终的效果如下:

练习题

粗体字为自定义题目

  1. 尝试交换三角形第一个和第三个顶点的数据,屏幕将显示什么?为什么?
  2. 尝试用6个顶点绘制矩形表面
  3. 尝试用4个顶点绘制矩形表面(提示:D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP

DirectX11 With Windows SDK完整目录

Github项目源码

欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。

DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区的更多相关文章

  1. 粒子系统与雨的效果 (DirectX11 with Windows SDK)

    前言 最近在学粒子系统,看这之前的<<3D图形编程基础 基于DirectX 11 >>是基于Direct SDK的,而DXSDK微软已经很久没有更新过了并且我学的DX11是用W ...

  2. OpenGL立方体在世界坐标系中_缩放_旋转_平移_顶点片源着色器_光照作用_棋盘纹理贴图

    读取bmp等图片格式中的像素还有难度,就先用这个棋盘图象素来弄了 代码打错一个就一直First-chance exception ,貌似还有一个要用q或者Q才能成功退出,不知道缺少哪句,我用窗口红叉退 ...

  3. WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码

    原文:WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 HLSL,High Level Shader Language,高级着色器语言,是 Di ...

  4. UnityShader之顶点片段着色器Vertex and Fragment Shader【Shader资料】

    顶点片段着色器 V&F Shader:英文全称Vertex and Fragment Shader,最强大的Shader类型,也是我们在使用ShaderLab中的重点部分,属于可编程管线,使用 ...

  5. HLSL像素着色器

    原文:HLSL像素着色器 昨日不可追, 今日尤可为.勤奋,炽诚,不忘初心 手机淘宝二维码 扫描       或者打开连接:程序设计开发 ,掌声鼓励,欢迎光临.     像素着色器替代了固定渲染管线的  ...

  6. D3D三层Texture纹理经像素着色器实现渲染YUV420P

    简单记录一下这两天用Texture实现渲染YUV420P的一些要点. 在视频播放的过程中,有的时候解码出来的数据是YUV420P的.表面(surface)通过设置参数是可以渲染YUV420P的,但Te ...

  7. 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但

    片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.

  8. ThreeJS 物理材质shader源码分析(像素着色器)

    再此之前推荐一款GLTF物理材质在线编辑器https://tinygltf.xyz/ 像素着色器(meshphysical_frag.glsl) #define PHYSICAL uniform ve ...

  9. DirectX11 With Windows SDK--00 目录

    前言 (更新于 2019/4/10) 从第一次接触DirectX 11到现在已经有将近两年的时间了.还记得前年暑假被要求学习DirectX 11,在用龙书的源码配置项目运行环境的时候都花了好几天的时间 ...

随机推荐

  1. MT【252】椭圆内接三角形内切圆半径

    已知椭圆$\dfrac{{{x^2}}}{{{a^2}}} + \dfrac{{{y^2}}}{{{b^2}}} = 1$($a > b > 0$),${F_1}$.${F_2}$为其左右 ...

  2. Age of Moyu HDU - 6386 (杭电多校7A)

    给出n和点,m条边,每条边有各自的标号,进入第一个标号需要消耗1的费用,此后转换标号需要1费用,在同一个标号上走不需要费用.问你从1到n最少需要多少费用. 最短路变形,把第一个点看成不存在的标号,然后 ...

  3. webpack入门(五)webpack CLI

    webpack的CLI安装和命令 Installation $ npm install webpack -g The webpack command is now available globally ...

  4. sql server 2000 个人版怎么在win7下安装

    win7 64位安装SQL2000个人版教程 首先,如果以前安装的话,要删除干净.我也找了半天的网络资料. 1.把原来sqlserver的安装目录 C:\Program Files\microsoft ...

  5. HDU/HDOJ 1867 A + B for you again

    仔细了解KMP之后再看这题就会发现是裸题. 因为kmp我们可以求出s的f数组,表示能与p的多少前缀匹配.那么我们只需取f[s.size() - 1]即可. #include <cstdio> ...

  6. 【LOJ#6277】数列分块1

    题目大意:维护一个长度为 N 的序列,支持区间修改.单点查询. 代码如下 #include <bits/stdc++.h> using namespace std; const int m ...

  7. FindExecutable:查找与一个指定文件关联在一起的程序的文件名

    百度百科:http://baike.baidu.com/view/1285880.htm ------------------------------------------------------- ...

  8. django2 用iframe标签完成 网页内嵌播放b站视频功能

    前言: 给自己的网站中加入视频资源,有两种方法,一种是用iframe标签引用外站资源,另一种则使用video标签,获取站内资源进行视频播放.其中前者顾名思义,是将视频资源上传到视频网站中,然后通过引用 ...

  9. 在html页面通过js实现复制粘贴功能

    前言:要实现这个功能,常用的方式大概分为两类,第一种就是上插件,这个网上有大把,第二种就是直接用几行JS来实现. 这次说第二种实现方式,这方式有很大的局限性,只能用表单元素,并且不能设置disable ...

  10. tensorflow-gpu版本出现libcublas.so.8.0:cannot open shared object file

    文章主要参考以下博客https://www.aliyun.com/zixun/wenji/1289957.html 在利用GPU加速tensorflow时,出现了libcublas.so.8.0:ca ...