Directx11教程(5) 画一个简单的三角形(1)
原文:Directx11教程(5) 画一个简单的三角形(1)
在本篇教程中,我们将通过D3D11画一个简单的三角形。在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作是由GPU固定管线完成,比如光栅化操作),最常用的shader操作是顶点shader(vertex shader)和像素shader(pixel shader)。其实shader就是在GPU中执行的代码,这些代码被driver编译成硬件依赖的机器码,最终被GPU中shader pipe执行,从而完成3D渲染。D3D11中shader是用一种类C的语言HLSL编写的。
下面我们先来了解几个概念:
三维物体的模型,通常3D对象都是通过mesh(三角形)来表示的。比如我们看到的一个渲染后的球体,实际上它是有许许多多的小的mesh组成。
顶点缓冲就是一个buffer,用来存放3D物体的顶点数据。
索引缓冲,就是对顶点缓冲的索引,可以用来减少渲染物体时候传入显存的顶点数量。比如一个正方体有8个顶点,同时也是由12个三角形组成(每个面2个三角形),我们渲染3D物体,在硬件层次都是通过三角形来渲染的,所以渲染这12个三角形,就需要36个顶点数据,但是通过索引缓冲,我们只需传入8个顶点,通过索引不同顺序的3个顶点,来实现12个三角形的渲染。
[GPU和系统内存之间,通过PCIE总线连接,传入数据受总线宽度的影响,所以我们要尽可能减少传输数据的数量]
程序的框架现在如下图所示:
我们增加了3个类:
ModelClass主要用来建立顶点缓冲、索引缓冲,准备渲染数据。
CamerClass主要用来得到view矩阵,就是得到摄像机在三维空间的位置和方位。
ColorShaderClass主要用来处理shader相关的代码。
下面我们将贴出关键的程序代码:
color.vs的代码如下:
/////////////
// GLOBALS //
//shader中使用的全局变量都在定义在const buffer中
//这样shader编译后,这些变量放在gpu的const buffer中
/////////////
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};
//////////////
// TYPEDEFS //
//注意:POSITION, COLOR等是我们在定义顶点布局时定义的名字。
//////////////
struct VertexInputType
{
float4 position : POSITION;
float4 color : COLOR;
};
struct PixelInputType
{
float4 position : SV_POSITION; //SV表示系统自动定义的格式。
float4 color : COLOR;
};
////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType ColorVertexShader(VertexInputType input)
{
PixelInputType output;
//顶点坐标扩展成四个分量,并设置为1,以便矩阵运算
input.position.w = 1.0f;
// 乘以3个矩阵,得到clip空间的坐标。
output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
//直接输出顶点的颜色(顶点之间的颜色,会在光栅化阶段采用插值的方式计算)
output.color = input.color;
return output;
}
color.ps的代码如下,代码非常简单,直接输出像素的颜色:
//////////////
// TYPEDEFS //
//像素输入格式
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ColorPixelShader(PixelInputType input) : SV_TARGET
{
return input.color;
}
GraphicsClass.h代码如下:
#pragma once
#include <windows.h>
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "colorshaderclass.h"
/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = false; //是否全屏
const bool VSYNC_ENABLED = true; //是否垂直同步
const float SCREEN_DEPTH = 1000.0f; //深度,远点
const float SCREEN_NEAR = 0.1f; //深度,近点
class GraphicsClass
{
public:
GraphicsClass(void);
GraphicsClass(const GraphicsClass&);
~GraphicsClass(void);
bool Initialize(int, int, HWND);
void Shutdown();
bool Frame();
private:
bool Render();
//定义一个D3DClass类成员变量
D3DClass* m_D3D;
CameraClass* m_Camera;
ModelClass* m_Model;
ColorShaderClass* m_ColorShader;
};
GraphicsClass.cpp主要代码如下:
首先会计算三个矩阵:世界矩阵(模型坐标空间到世界坐标空间转化矩阵)、视点矩阵(世界坐标空间转化到视点坐标空间、或者说是摄像机坐标空间)、投影矩阵(视点坐标空间进行投影操作,在透视投影情况下,产生一个称作frustum的视锥体,在平行投影的情况下,产生一个长方体)。
然后在Shader类中执行具体的渲染操作。
bool GraphicsClass::Render()
{
D3DXMATRIX viewMatrix, projectionMatrix, worldMatrix;
bool result;
// 设置framebuffer.为浅蓝色
m_D3D->BeginScene(0.0f, 0.0f, 0.5f, 1.0f);
// 得到view矩阵.
m_Camera->Render();
// 得到3个矩阵.
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
// 把模型顶点和索引缓冲放入管线,准备渲染.
m_Model->Render(m_D3D->GetDeviceContext());
// 用shader渲染.
result = m_ColorShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix);
if(!result)
{
return false;
}
//把framebuffer中的图像present到屏幕上.
m_D3D->EndScene();
return true;
}
ModelClass类的关键函数是InitializeBuffers和RenderBuffer,在初始化函数中产生顶点缓冲和索引缓冲,在RenderBuffer函数中,把缓冲绑定到管线,并且指定渲染体元类型。
bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
VertexType* vertices;
unsigned long* indices;
D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
D3D11_SUBRESOURCE_DATA vertexData, indexData;
HRESULT result;
//首先,我们创建2个临时缓冲存放顶点和索引数据,以便后面使用。.
// 设置顶点缓冲大小为3,一个三角形.
m_vertexCount = 3;
// 设置索引缓冲大小.
m_indexCount = 3;
// 创建顶点临时缓冲.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
return false;
}
// 创建索引缓冲.
indices = new unsigned long[m_indexCount];
if(!indices)
{
return false;
}
//创建顺时针方向的三角形,左手规则
// 设置顶点数据.
vertices[0].position = D3DXVECTOR3(-1.0f, -1.0f, 0.0f); // 左下
vertices[0].color = D3DXVECTOR4(1.0f, 1.0f, 0.0f, 1.0f);
vertices[1].position = D3DXVECTOR3(0.0f, 1.0f, 0.0f); // 中上.
vertices[1].color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);
vertices[2].position = D3DXVECTOR3(1.0f, -1.0f, 0.0f); // 底右
vertices[2].color = D3DXVECTOR4(0.0f, 1.0f, 1.0f, 1.0f);
// 设置索引缓冲数据.
indices[0] = 0; // Bottom left.
indices[1] = 1; // Top middle.
indices[2] = 2; // Bottom right.
// 设置顶点缓冲描述
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
// 指向保存顶点数据的临时缓冲.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
// 创建顶点缓冲.
result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
if(FAILED(result))
{
return false;
}
// 设置索引缓冲描述.
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
// 指向存临时索引缓冲.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// 创建索引缓冲.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
if(FAILED(result))
{
return false;
}
// 释放临时缓冲.
delete [] vertices;
vertices = 0;
delete [] indices;
indices = 0;
return true;
}
void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
unsigned int stride;
unsigned int offset;
// 设置顶点缓冲跨度和偏移.
stride = sizeof(VertexType);
offset = 0;
//在input assemberl阶段绑定顶点缓冲,以便能够被渲染
deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
//在input assemberl阶段绑定索引缓冲,以便能够被渲染
deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// 设置体元语义,渲染三角形列表.
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
return;
}
CameraClass类主要是得到视图矩阵,代码就不贴了。
下面是本章的关键部分,shader代码部分,主要包括打开shader文件,编译shader文件,绑定常量缓冲,定义顶点布局,调用shader执行渲染操作等等。
ColorShaderClass.cpp主要代码如下:
bool ColorShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix,
D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
bool result;
这几个shader参数是我们在vs中定义在const buffer中的。
// 设置shader参数.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix);
if(!result)
{
return false;
}
// 用shader渲染指定缓冲顶点
RenderShader(deviceContext, indexCount);
return true;
}
bool ColorShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
HRESULT result;
ID3D10Blob* errorMessage;
ID3D10Blob* vertexShaderBuffer;
ID3D10Blob* pixelShaderBuffer;
D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
unsigned int numElements;
D3D11_BUFFER_DESC matrixBufferDesc;
// 初始化指针为空.
errorMessage = 0;
vertexShaderBuffer = 0;
pixelShaderBuffer = 0;
// 编译vs代码.
result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "ColorVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
&vertexShaderBuffer, &errorMessage, NULL);
if(FAILED(result))
{
// 如果vs编译失败,输出错误消息.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
}
// 如果没有任何错误消息,可能是shader文件丢失.
else
{
MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
}
return false;
}
// 编译ps.
result = D3DX11CompileFromFile(psFilename, NULL, NULL, "ColorPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
&pixelShaderBuffer, &errorMessage, NULL);
if(FAILED(result))
{
// 如果ps编译失败,输出错误信息.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
}
// 如果没有任何错误消息,可能是shader文件丢失.
else
{
MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
}
return false;
}
// 从缓冲创建vs shader.
result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader);
if(FAILED(result))
{
return false;
}
// 从缓冲创建ps shader.
result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader);
if(FAILED(result))
{
return false;
}
// 设置数据布局layout,以便在shader中使用.
// 定义要和ModelClass中的顶点结构一致.
polygonLayout[0].SemanticName = "POSITION"; //vs中的输入参数
polygonLayout[0].SemanticIndex = 0;
polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[0].InputSlot = 0;
polygonLayout[0].AlignedByteOffset = 0;
polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[0].InstanceDataStepRate = 0;
polygonLayout[1].SemanticName = "COLOR";
polygonLayout[1].SemanticIndex = 0;
polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
polygonLayout[1].InputSlot = 0;
polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[1].InstanceDataStepRate = 0;
// 得到layout中的元素数量
numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// 创建顶点输入布局.
result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(),
vertexShaderBuffer->GetBufferSize(), &m_layout);
if(FAILED(result))
{
return false;
}
//释放顶点和像素缓冲.
vertexShaderBuffer->Release();
vertexShaderBuffer = 0;
pixelShaderBuffer->Release();
pixelShaderBuffer = 0;
// 设置动态矩阵描述.
matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixBufferDesc.MiscFlags = 0;
matrixBufferDesc.StructureByteStride = 0;
// 创建const buffer指针,以便访问shader常量.
result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
if(FAILED(result))
{
return false;
}
return true;
}
bool ColorShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix,
D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;
// 传入shader前,确保矩阵转置,这是D3D11的要求.
D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);
// 锁定常量缓冲,以便能够写入.
result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// 得到const buffer指针.
dataPtr = (MatrixBufferType*)mappedResource.pData;
// 设置world,view以及projection矩阵.
dataPtr->world = worldMatrix;
dataPtr->view = viewMatrix;
dataPtr->projection = projectionMatrix;
// 解锁常量缓冲.
deviceContext->Unmap(m_matrixBuffer, 0);
// 设置常量缓冲位置.
bufferNumber = 0;
// 用更新后的值设置常量缓冲.
deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);
return true;
}
DrawIndexed函数会根据指定的索引缓冲、体元类型、相关shader,向GPU传送一个draw primitive的命令,从而真正开始体元的渲染操作。
void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
// 绑定顶点布局.
deviceContext->IASetInputLayout(m_layout);
// 设置渲染使用vs和ps.
deviceContext->VSSetShader(m_vertexShader, NULL, 0);
deviceContext->PSSetShader(m_pixelShader, NULL, 0);
// 渲染三角形
deviceContext->DrawIndexed(indexCount, 0, 0);
return;
}
程序运行后如下图所示:
完整的代码请参考:
工程文件myTutorialD3D11_4
代码下载:
http://files.cnblogs.com/mikewolf2002/myTutorialD3D11.zip
Directx11教程(5) 画一个简单的三角形(1)的更多相关文章
- Directx11教程(6) 画一个简单的三角形(2)
原文:Directx11教程(6) 画一个简单的三角形(2) 在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变, ...
- Directx11教程(19) 画一个简单的地形
原文:Directx11教程(19) 画一个简单的地形 通常我们在xz平面定义一个二维的网格,然后y的值根据一定的函数计算得到,比如正弦.余弦函数的组合等等,可以得到一个看似不错的地形或者 ...
- Directx11学习笔记【十一】 画一个简单的三角形--effect框架的使用
这里不再介绍effect框架的具体使用,有关effect框架使用可参考http://www.cnblogs.com/zhangbaochong/p/5475961.html 实现的功能依然是画一个简单 ...
- Directx11教程(7) 画一个颜色立方体
原文:Directx11教程(7) 画一个颜色立方体 前面教程我们通过D3D11画了一个三角形,本章我们将画一个颜色立方体,它的立体感更强.主要的变动是ModelClass类,在Model ...
- Directx11教程(10) 画一个简易坐标轴
原文:Directx11教程(10) 画一个简易坐标轴 本篇教程中,我们将在三维场景中,画一个简易的坐标轴,分别用红.绿.蓝三种颜色表示x,y,z轴的正向坐标轴. 为此,我们要先建立一个A ...
- Directx11学习笔记【十】 画一个简单的三角形
本篇笔记要实现的是在屏幕上渲染出一个三角形,重点要学习的是渲染一个几何体的流程方式. 为了渲染几何图形,需要一个顶点缓存和一个描述顶点布局的输入层,还有着色器(主要是顶点着色器和像素着色器),下面来看 ...
- Directx11教程(56) 建立一个skydome
原文:Directx11教程(56) 建立一个skydome 本章建立一个skydome(天空穹),主要学习如何使用cube mapping. cube map就是把六张纹理当作 ...
- Directx11教程(42) 纹理映射(12)-简单的bump mapping
原文:Directx11教程(42) 纹理映射(12)-简单的bump mapping 有时候,我们只有一个粗糙的模型,但是我们想渲染纹理细节,比如一个砖墙,我们如何在只有一个平面的时候 ...
- Directx11教程(11) 增加一个debug宏
原文:Directx11教程(11) 增加一个debug宏 现在我们在common.h中增加一个debug的宏,在每个d3d11函数后调用,如果d3d函数出错,它能够给出程序中错误的代码行 ...
随机推荐
- IDEA修改Servlet代码模板
- sql作业启停服务器
IF EXISTS(SELECT * FROM msdb.dbo.sysjobs WHERE name='启用pubs数据库') EXEC msdb.dbo.sp_delete_job @job_na ...
- 使用uni-app(Vue.js)创建运行微信小程序项目步骤
使用uni-app(Vue.js)开发微信小程序项目步骤 1. 新建一个uni-app项目 创建完成后的目录结构 2. 打开微信小程序开发工具端的端口调试功能 3. 运行创建的项目 效果
- JavaScript中this的指向(转载)
转载自:http://www.cnblogs.com/dongcanliang/p/7054176.html 前言 this 指向问题是入坑前端必须了解知识点,现在迎来了ES6时代,因为箭头函数的出现 ...
- JS random函数深入理解(转载)
转载自:(本文对读者有帮助的话请移步支持原作者) http://www.cnblogs.com/starof/p/4988516.html 一.预备知识 Math.ceil(); //向上取整. M ...
- os模块和sys模块
1.os模块与path有关:os.path.isfile():判断置顶对象是否为文件,是返回True,否返回Falseos.path.isdir():判断指定对象是否为目录,是返回True,否返回Fa ...
- Leetcode12.Integer to Roman整数转罗马数字
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并 ...
- bzoj 4521 [Cqoi2016]手机号码——数位dp
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4521 dfs真好用~ #include<iostream> #include&l ...
- JS---案例:手风琴 (利用封装好的动画函数)
案例:手风琴 封装好的动画函数在common.js里面 //function getStyle(element, attr) {...} //function animate( ...
- ecshop二次开发之电子票
前台效果展示: 2. 3. 后台展示效果: 代码实现: 一. 添加菜单项:路径admin\includes\inc_menu.PHP $modules['18_ticket_m ...