【DirectX12从零渲染04】DirectX12 三角形的绘制

2024-04-14 19:20

本文主要是介绍【DirectX12从零渲染04】DirectX12 三角形的绘制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DirectX12 三角形的绘制

  • 一、Dx12三角形的绘制
  • 二、准备资产(InitAsset)
    • 1、准备网格体(BuildMeshData)
      • (1)定义三角形
      • (2)创建内存存放区
      • (3)资源合入默认堆
    • 2、准备渲染管线(BuildPSO)
      • (1)定义输入布局
      • (2)定义跟签名
      • (3)定义shader
      • (4)定义其他设置
  • 三、核心绘制(SubmitDrawingTaskCore)
    • 1、提交单绑定PSO
    • 2、提交单绘制资源命令添加
  • 三、整体代码
  • 四、效果展示

一、Dx12三角形的绘制

上一章说完了Dx12的绘制总流程,但只有一个背景板,因此我们在这章要绘制一个三角形。

在这一阶段中,主要就是补充完上一次总流程的一个核心步骤,但对此步骤,我们是要前面有些准备的,如下:
1、准备资产;
2、核心绘制;

二、准备资产(InitAsset)

我们想要绘制一个三角形,就要去向dx12要,而dx12会提出两个问题:
1、想要一个什么样的三角形
2、三角形需要什么加工

这两个问题的回答,就是 准备网格体(Mesh)和 准备渲染管线(PipeLineStateObject)。

void cDx12Rendering::InitAsset()
{//准备网格体BuildMeshData();//准备渲染管线BuildPSO();
}

1、准备网格体(BuildMeshData)

(1)定义三角形

首先,需要我们先去定义一个三角形的基础数据结构,也就是确定具有那些基础属性,比如位置,比如颜色

struct cVertex
{cVertex(const XMFLOAT3& InPos, const XMFLOAT4& InColor){m_mPosition = InPos;m_mColor = InColor;}XMFLOAT3 m_mPosition;XMFLOAT4 m_mColor;
};

然后,创建根据数据结构,创建出这个三角形,如下:

cVertex triangleVertices[] =
{{ { 0.0f, 0.5f, 0.0f }, XMFLOAT4(Colors::Red) },{ { 0.5f, -0.5f, 0.0f }, XMFLOAT4(Colors::Green)},{ { -0.5f, -0.5f, 0.0f }, XMFLOAT4(Colors::Blue) }
};

诚然,我们是不能直接将vertex buffer绑定到pipeline上,而需要使用descriptor,即在Dx12中使用 D3D12_VERTEX_BUFFER_VIEW 结构体表示:

const UINT triangleVerticesSize = sizeof(triangleVertices);m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.SizeInBytes = triangleVerticesSize;
m_vertexBufferView.StrideInBytes = sizeof(cVertex);

(2)创建内存存放区

这里涉及到了CPU和GPU的内存交互,如下图:
在这里插入图片描述
理论上上传资源应该是使用upload heap,但由于我们很多资源是不会改变的,而GPU每帧都要读取上传堆里面的资源,就会造成性能问题。
因此针对于这类数据来自于CPU,且属于 静态数据 的情况,我们往往是先创建一个resource在default heap,然后在创建一个resource在upload heap作媒介,通过upload heap的resource将CPU数据传输到default heap的resource上,后续GPU只需要读取default heap上的resource即可,达到最优性能。

//默认堆(default heap),处在显存中的
CD3DX12_RESOURCE_DESC BufferResourceDESC = CD3DX12_RESOURCE_DESC::Buffer(triangleVerticesSize);
CD3DX12_HEAP_PROPERTIES BufferProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);m_spD3dDevice->CreateCommittedResource(&BufferProperties,D3D12_HEAP_FLAG_NONE,&BufferResourceDESC,D3D12_RESOURCE_STATE_COMMON,nullptr,IID_PPV_ARGS(&m_vertexBuffer));
//上传堆(upload heap),处在共享内存中的
ComPtr<ID3D12Resource> vertexUploadBuffer;CD3DX12_HEAP_PROPERTIES UpdateBufferProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
m_spD3dDevice->CreateCommittedResource(&UpdateBufferProperties,D3D12_HEAP_FLAG_NONE,&BufferResourceDESC,D3D12_RESOURCE_STATE_GENERIC_READ,nullptr,IID_PPV_ARGS(&vertexUploadBuffer));

可以通过 ID3D12Device::CreateCommittedResource 函数,创建一个resource以及一个隐式的heap(此heap指的并不是用来存放descriptor的descriptor heap)。
resource会被映射到这个heap上,该heap有足够大的空间包含整个resource。

(3)资源合入默认堆

这一步骤,就是将我们确定的三角形资源合并到我们的提交列表里面,这就要涉及到部分提交列表的部分,鉴于第三章已经讲过,就不再赘述。

// 设置要传输的CPU数据
D3D12_SUBRESOURCE_DATA subResourceData = {};
subResourceData.pData = triangleVertices;
subResourceData.RowPitch = triangleVerticesSize;
subResourceData.SlicePitch = subResourceData.RowPitch;// 之前close了command list,这里要使用到,需要reset操作才可记录command
m_spGraphicsCommandList->Reset(m_spCommandAllocator.Get(), nullptr); CD3DX12_RESOURCE_BARRIER CopyDestBarrier = CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(),D3D12_RESOURCE_STATE_COMMON,D3D12_RESOURCE_STATE_COPY_DEST);//传输前要更改resource的state
m_spGraphicsCommandList->ResourceBarrier(1, &CopyDestBarrier);//会先将CPU内存数据拷贝到upload heap中,然后再通过ID3D12CommandList::CopySubresourceRegion从upload heap中拷贝到default buffer中
UpdateSubresources<1>(m_spGraphicsCommandList.Get(),m_vertexBuffer.Get(),vertexUploadBuffer.Get(),0,0,1,&subResourceData);CD3DX12_RESOURCE_BARRIER ReadDestBarrier = CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(),D3D12_RESOURCE_STATE_COPY_DEST,D3D12_RESOURCE_STATE_GENERIC_READ);m_spGraphicsCommandList->ResourceBarrier(1, &ReadDestBarrier);m_spGraphicsCommandList->Close();
ID3D12CommandList* cmdsLists[] = { m_spGraphicsCommandList.Get() };
m_spCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);WaitGPUComplete();

这一部分重点就在于,我们要如何通过upload heap将resource传输到default heap里;
dx12为我们提供了 UpdateSubresources 函数来实现这样的操作,在一次完整的提交操作中插入这个指令即可。

2、准备渲染管线(BuildPSO)

在准备渲染管线的阶段,其主要就是围绕 PSO(渲染管线状态对象,Pipeline State Object)的构建而进行。

ComPtr<ID3D12PipelineState> m_pipelineState;D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
...
...
...
m_spD3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState));

(1)定义输入布局

由于是我们自己定义的结构体,必然是要提供一份“使用说明”给Dx12的。这个使用说明就是结构体D3D12_INPUT_LAYOUT_DESC 。
注意,这部分会与后面的shader部分的 hlsl文件 强关联。

//InputLayout
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};D3D12_INPUT_LAYOUT_DESC inputLayout = { inputElementDescs, _countof(inputElementDescs) };
psoDesc.InputLayout = inputLayout;

(2)定义跟签名

Root Signature 用来配置 Shader 需要的 Resource ,例如 constant buffer。
本例子暂时用不到,利用下面代码创建一个空的 root signature:

//Root Signature
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);ComPtr<ID3DBlob> signature, error;
D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);ComPtr<ID3D12RootSignature> rootSignature;
m_spD3dDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature));psoDesc.pRootSignature = rootSignature.Get();

(3)定义shader

Shader(着色器)在可编程渲染流水线中,所处的位置是顶点着色器(VS)和片元着色器(PS),这两个部分是高度可编程的。
因此,此阶段我们的目标主要是把这两个部分定义好,具体就是把指定操作从 hlsl 文件中读出来,做成 ID3DBlob ,最后绑定在管线上。

首先是定义标志,毕竟hlsl里面不一定不会错,DEBUG帮助调试。

#if defined(_DEBUG)
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif

后续就是固定流程,定义ID3DBlob,读取指定hlsl文件,绑定到管线的三步走。

ComPtr<ID3DBlob> vsByteCode;D3DCompileFromFile(
L"E:/Code/Dx12Test0302/Dx12Text/Debug/shaders.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vsByteCode, nullptr);//绑定顶点着色器代码
psoDesc.VS.pShaderBytecode = reinterpret_cast<BYTE*>(vsByteCode->GetBufferPointer());
psoDesc.VS.BytecodeLength = vsByteCode->GetBufferSize();
ComPtr<ID3DBlob> psByteCode;D3DCompileFromFile(
L"E:/Code/Dx12Test0302/Dx12Text/Debug/shaders.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &psByteCode, nullptr);//绑定像素着色器
psoDesc.PS.pShaderBytecode = psByteCode->GetBufferPointer();
psoDesc.PS.BytecodeLength = psByteCode->GetBufferSize();

最后,别忘了新建一个hlsl文件,里面具体的解释,就很有内容了,还是先放到后面仔细去说,大框架如下:
a、定义输入(PSInput)
b、定义顶点如何处理的函数(VSMain):
c、定义像素如何处理的函数(PSMain)

struct PSInput
{float4 position : SV_POSITION;float4 color : COLOR;
};PSInput VSMain(float3 position : POSITION, float4 color : COLOR)
{PSInput result;result.position = float4(position, 1.0f);result.color = color;return result;
}float4 PSMain(PSInput input) : SV_TARGET
{return input.color;
}

(4)定义其他设置

rendering pipeline 中很多阶段是可编程的(例如vertex shader,pixel shader),但是有些阶段我们只能修改它们的设置。我们可以通过 D3D12_RASTERIZER_DESC 来对 rasterization 阶段进行设置。

d3dx12.h中为我们提供了CD3DX12_RASTERIZER_DESC类,继承于D3D12_RASTERIZER_DESC,我们可以用其快速的生成一个全是默认值的D3D12_RASTERIZER_DESC对象:

//Other SettingpsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);//混合状态,深度测试设置
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);//模板测试关闭
psoDesc.DepthStencilState.StencilEnable = FALSE;psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;//绑定RTV和DSV
psoDesc.RTVFormats[0] = m_dfBackBufferFormat;
psoDesc.DSVFormat = m_dfDepthStencilFormat;//确定交换链个数
psoDesc.SampleDesc.Count = 2;

三、核心绘制(SubmitDrawingTaskCore)

上次,我们说 提交绘制任务 是 绘制 的绝对核心,但当时只是画了一个背景板,现在我们要做核心的核心,也就是用准备好的资源画一个三角形出来。

1、提交单绑定PSO

void cDx12Rendering::ResetCMDListAlloctor()
{//重置录制相关的内存,为下一帧做准备...m_spGraphicsCommandList->Reset(m_spCommandAllocator.Get(), m_pipelineState.Get());
}

2、提交单绘制资源命令添加

当GPU拥有了vertex buffer后,却还是不知道应该如何绘制,其原因是vertex顶点有很多,但GPU不知道画成点就可以,还是每两个vertex画一条线,亦或者每三个vertex画一个三角形?
因此我们需要通过 IASetPrimitiveTopology 函数来告诉GPU该如何使用vertex buffer进行绘制。

m_spGraphicsCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

设置好Primitive Topology后,就可以通过 IASetVertexBuffers 函数将vertex buffer绑定到pipeline的input assembler阶段的input slot上了。
输入装配(Input Assembler,简称 IA)阶段从内存读取几何数据(顶点和索引)并将这些数据组合为几何图元(例如,三角形、直线)。

m_spGraphicsCommandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);

通过IASetVertexBuffers函数我们仅仅是将vertex buffer准备好进入pipeline,最后我们需要通过 DrawInstanced 函数来进行实际上的绘制。

m_spGraphicsCommandList->DrawInstanced(3, 1, 0, 0);

三、整体代码

BOOL cDx12Rendering::Init(cDx12RenderConfig* io_cDx12RenderConfig)
{//第二章初始化步骤的函数...//补充 准备资产的函数InitAsset();return TRUE;
}
void cDx12Rendering::InitAsset()
{//准备网格体BuildMeshData();//准备渲染管线BuildPSO();
}
oid cDx12Rendering::BuildMeshData()
{cVertex triangleVertices[] ={{ { 0.0f, 0.5f, 0.0f }, XMFLOAT4(Colors::Red) },{ { 0.5f, -0.5f, 0.0f }, XMFLOAT4(Colors::Green)},{ { -0.5f, -0.5f, 0.0f }, XMFLOAT4(Colors::Blue) }};ComPtr<ID3D12Resource> vertexUploadBuffer;const UINT triangleVerticesSize = sizeof(triangleVertices);CD3DX12_RESOURCE_DESC BufferResourceDESC = CD3DX12_RESOURCE_DESC::Buffer(triangleVerticesSize);CD3DX12_HEAP_PROPERTIES BufferProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);m_spD3dDevice->CreateCommittedResource(&BufferProperties,D3D12_HEAP_FLAG_NONE,&BufferResourceDESC,D3D12_RESOURCE_STATE_COMMON,nullptr,IID_PPV_ARGS(&m_vertexBuffer));CD3DX12_HEAP_PROPERTIES UpdateBufferProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);m_spD3dDevice->CreateCommittedResource(&UpdateBufferProperties,D3D12_HEAP_FLAG_NONE,&BufferResourceDESC,D3D12_RESOURCE_STATE_GENERIC_READ,nullptr,IID_PPV_ARGS(&vertexUploadBuffer));// 设置要传输的CPU数据D3D12_SUBRESOURCE_DATA subResourceData = {};subResourceData.pData = triangleVertices;subResourceData.RowPitch = triangleVerticesSize;subResourceData.SlicePitch = subResourceData.RowPitch;m_spGraphicsCommandList->Reset(m_spCommandAllocator.Get(), nullptr); // 之前close了command list,这里要使用到,需要reset操作才可记录commandCD3DX12_RESOURCE_BARRIER CopyDestBarrier = CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(),D3D12_RESOURCE_STATE_COMMON,D3D12_RESOURCE_STATE_COPY_DEST);//传输前要更改resource的statem_spGraphicsCommandList->ResourceBarrier(1, &CopyDestBarrier);//会先将CPU内存数据拷贝到upload heap中,然后再通过ID3D12CommandList::CopySubresourceRegion从upload heap中拷贝到default buffer中UpdateSubresources<1>(m_spGraphicsCommandList.Get(),m_vertexBuffer.Get(),vertexUploadBuffer.Get(),0,0,1,&subResourceData);CD3DX12_RESOURCE_BARRIER ReadDestBarrier = CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(),D3D12_RESOURCE_STATE_COPY_DEST,D3D12_RESOURCE_STATE_GENERIC_READ);//m_spGraphicsCommandList->ResourceBarrier(1, &ReadDestBarrier);m_spGraphicsCommandList->Close();ID3D12CommandList* cmdsLists[] = { m_spGraphicsCommandList.Get() };m_spCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);WaitGPUComplete();m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();m_vertexBufferView.SizeInBytes = triangleVerticesSize;m_vertexBufferView.StrideInBytes = sizeof(cVertex);
}
void cDx12Rendering::BuildPSO()
{D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};//InputLayoutD3D12_INPUT_ELEMENT_DESC inputElementDescs[] ={{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }};D3D12_INPUT_LAYOUT_DESC inputLayout = { inputElementDescs, _countof(inputElementDescs) };psoDesc.InputLayout = inputLayout;//Root SignatureCD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);ComPtr<ID3DBlob> signature, error;D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);ComPtr<ID3D12RootSignature> rootSignature;m_spD3dDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature));psoDesc.pRootSignature = rootSignature.Get();//Shader CompilerComPtr<ID3DBlob> vsByteCode, psByteCode;#if defined(_DEBUG)UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#elseUINT compileFlags = 0;
#endifD3DCompileFromFile(L"E:/Code/Dx12Test0302/Dx12Text/Debug/shaders.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vsByteCode, nullptr);D3DCompileFromFile(L"E:/Code/Dx12Test0302/Dx12Text/Debug/shaders.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &psByteCode, nullptr);//绑定顶点着色器代码psoDesc.VS.pShaderBytecode = reinterpret_cast<BYTE*>(vsByteCode->GetBufferPointer());psoDesc.VS.BytecodeLength = vsByteCode->GetBufferSize();//绑定像素着色器psoDesc.PS.pShaderBytecode = psByteCode->GetBufferPointer();psoDesc.PS.BytecodeLength = psByteCode->GetBufferSize();//Other SettingpsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);psoDesc.DepthStencilState.DepthEnable = FALSE;psoDesc.DepthStencilState.StencilEnable = FALSE;psoDesc.SampleMask = UINT_MAX;psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;psoDesc.NumRenderTargets = 1;psoDesc.RTVFormats[0] = m_dfBackBufferFormat;psoDesc.DSVFormat = m_dfDepthStencilFormat;psoDesc.SampleDesc.Count = 2;m_spD3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState));
}
void cDx12Rendering::SubmitDrawingTask()
{//把后缓冲区的资源状态切换成Render Target...//设置视口和裁剪区域。...//清空后缓存和深度缓存。...//输出的合并阶段...SubmitDrawingTaskCore();//把后缓冲区切换成PRESENT状态...//录入完成...//提交命令...
}
void cDx12Rendering::SubmitDrawingTaskCore()
{	m_spGraphicsCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);m_spGraphicsCommandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);m_spGraphicsCommandList->DrawInstanced(3, 1, 0, 0);
}

四、效果展示

在这里插入图片描述

PS:留个问题,蓝色背景可能太喧宾夺主了,那怎么把这蓝色背景改成灰色呢?

这篇关于【DirectX12从零渲染04】DirectX12 三角形的绘制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/903790

相关文章

详解如何在React中执行条件渲染

《详解如何在React中执行条件渲染》在现代Web开发中,React作为一种流行的JavaScript库,为开发者提供了一种高效构建用户界面的方式,条件渲染是React中的一个关键概念,本文将深入探讨... 目录引言什么是条件渲染?基础示例使用逻辑与运算符(&&)使用条件语句列表中的条件渲染总结引言在现代

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

使用Python绘制可爱的招财猫

《使用Python绘制可爱的招财猫》招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常出现在亚洲文化的商店、餐厅和家庭中,今天,我将带你用Python和matplotlib库从零开始绘制一... 目录1. 为什么选择用 python 绘制?2. 绘图的基本概念3. 实现代码解析3.1 设置绘图画

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

如何用Python绘制简易动态圣诞树

《如何用Python绘制简易动态圣诞树》这篇文章主要给大家介绍了关于如何用Python绘制简易动态圣诞树,文中讲解了如何通过编写代码来实现特定的效果,包括代码的编写技巧和效果的展示,需要的朋友可以参考... 目录代码:效果:总结 代码:import randomimport timefrom math

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

取得 Git 仓库 —— Git 学习笔记 04

取得 Git 仓库 —— Git 学习笔记 04 我认为, Git 的学习分为两大块:一是工作区、索引、本地版本库之间的交互;二是本地版本库和远程版本库之间的交互。第一块是基础,第二块是难点。 下面,我们就围绕着第一部分内容来学习,先不考虑远程仓库,只考虑本地仓库。 怎样取得项目的 Git 仓库? 有两种取得 Git 项目仓库的方法。第一种是在本地创建一个新的仓库,第二种是把其他地方的某个

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

CSS实现DIV三角形

本文内容收集来自网络 #triangle-up {width: 0;height: 0;border-left: 50px solid transparent;border-right: 50px solid transparent;border-bottom: 100px solid red;} #triangle-down {width: 0;height: 0;bor