news 2026/5/9 18:09:32

通用GUI编程技术——图形渲染实战(四十二)——混合与透明渲染:从Alpha混合到粒子系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
通用GUI编程技术——图形渲染实战(四十二)——混合与透明渲染:从Alpha混合到粒子系统

通用GUI编程技术——图形渲染实战(四十二)——混合与透明渲染:从Alpha混合到粒子系统

仓库已经开源!喜欢的话点个⭐!包含Win32的目前已完成教程,力争做一个完备的GUI教程!

欢迎各位大佬前来参观:https://github.com/Charliechen114514/anatomy_gui

在上一篇文章中,我们用 Phong 光照模型让立方体有了真实的明暗感——环境光打底、漫反射计算亮度、镜面高光带来那一道闪亮。但到目前为止,我们画的所有东西都是不透明的:后画的覆盖先画的,深度缓冲帮我们解决遮挡问题,世界就这么简单。

现实世界可没这么简单。窗户玻璃是半透明的,烟雾和火焰有透明的部分,粒子特效的每一颗粒子边缘都要和背景平滑融合。今天我们要解决的就是 Alpha 混合——如何让 GPU 把"新颜色"和"旧颜色"按比例混合在一起,以及透明渲染带来了哪些不透明的渲染永远不会遇到的坑。

环境说明

  • 操作系统: Windows 10/11
  • 编译器: MSVC (Visual Studio 2022)
  • 图形库: Direct3D 11(链接d3d11.libd3dcompiler.lib
  • 前置知识: 文章 41(光照模型基础),文章 40(深度缓冲与 3D 变换)

理解 Alpha 混合的数学本质

在开始写代码之前,我们必须先搞清楚 Alpha 混合到底在数学上做了什么。D3D11 的混合阶段(Output Merger)是渲染管线的最后一个阶段——像素着色器输出一个颜色,混合阶段把这个颜色和渲染目标上已有的颜色进行组合。组合的公式是:

finalColor = srcColor * srcBlendFactor + dstColor * dstBlendFactor

其中srcColor是像素着色器输出的颜色(源颜色),dstColor是渲染目标上当前的颜色(目标颜色),srcBlendFactordstBlendFactor是混合因子,它们决定了源和目标各自的权重。

最经典的 Alpha 混合配置是让源颜色乘以源 Alpha,目标颜色乘以 1 减去源 Alpha:

finalColor = srcColor * srcAlpha + dstColor * (1 - srcAlpha)

这就是所谓的"标准透明混合"。当srcAlpha = 1.0时,最终颜色就是源颜色(完全不透明);当srcAlpha = 0.0时,最终颜色就是目标颜色(完全透明);当srcAlpha = 0.5时,源和目标各占一半。

D3D11 还允许对 Alpha 通道单独配置混合公式,但在绝大多数场景下,Alpha 通道的混合和颜色通道保持一致就够了。需要注意的是,D3D11 用D3D11_BLEND_DESC结构体来描述混合状态,它可以为每个渲染目标独立配置混合参数——如果你有多个渲染目标(MRT),灵活性是有的,但在我们的场景中只用一个渲染目标。

第一步——创建透明混合状态

在 D3D11 中,混合行为由ID3D11BlendState对象控制。默认情况下,D3D11 使用的是不透明的混合模式——也就是源颜色直接覆盖目标颜色。要让透明混合生效,我们需要创建一个自定义的混合状态并绑定到管线。

创建混合状态的代码如下:

D3D11_BLEND_DESC blendDesc={};blendDesc.RenderTarget[0].BlendEnable=TRUE;// 源颜色 = 源颜色 * 源AlphablendDesc.RenderTarget[0].SrcBlend=D3D11_BLEND_SRC_ALPHA;// 目标颜色 = 目标颜色 * (1 - 源Alpha)blendDesc.RenderTarget[0].DestBlend=D3D11_BLEND_INV_SRC_ALPHA;// 颜色混合操作:加法blendDesc.RenderTarget[0].BlendOp=D3D11_BLEND_OP_ADD;// Alpha通道的混合:同样使用标准公式blendDesc.RenderTarget[0].SrcBlendAlpha=D3D11_BLEND_ONE;blendDesc.RenderTarget[0].DestBlendAlpha=D3D11_BLEND_INV_SRC_ALPHA;blendDesc.RenderTarget[0].BlendOpAlpha=D3D11_BLEND_OP_ADD;blendDesc.RenderTarget[0].RenderTargetWriteMask=D3D11_COLOR_WRITE_ENABLE_ALL;ComPtr<ID3D11BlendState>pBlendState;HRESULT hr=g_pDevice->CreateBlendState(&blendDesc,&pBlendState);if(FAILED(hr)){// 创建失败returnfalse;}

我们来逐字段解读一下。BlendEnable = TRUE打开混合,这是最基本的开关。SrcBlend = D3D11_BLEND_SRC_ALPHA表示源颜色乘以源 Alpha 值,DestBlend = D3D11_BLEND_INV_SRC_ALPHA表示目标颜色乘以1 - srcAlphaBlendOp = D3D11_BLEND_OP_ADD表示两者相加。这三个字段组合起来就实现了前面提到的标准透明混合公式。

Alpha 通道的混合参数略有不同——SrcBlendAlpha = D3D11_BLEND_ONE意味着源 Alpha 直接保留(不乘以任何系数),目标 Alpha 乘以1 - srcAlpha。这样做的原因是 Alpha 通道代表的是"透明度",我们希望新的 Alpha 值反映的是混合后的实际透明度。

创建好混合状态后,在渲染时绑定它:

// 绑定混合状态(最后两个参数是混合因子和采样掩码,通常用默认值)floatblendFactor[4]={0.0f,0.0f,0.0f,0.0f};g_pContext->OMSetBlendState(pBlendState.Get(),blendFactor,0xFFFFFFFF);

OMSetBlendState是 Output Merger 阶段设置混合状态的函数。blendFactor用于某些特殊的混合模式(比如D3D11_BLEND_BLEND_FACTOR),在标准透明混合中用不到,传全零就行。最后一个参数是采样掩码,用于多重采样(MSAA)场景,全 1 表示所有采样点都参与。

当你需要恢复到不透明渲染时,只需传nullptr作为混合状态:

g_pContext->OMSetBlendState(nullptr,nullptr,0xFFFFFFFF);

根据 Microsoft Learn - ID3D11DeviceContext::OMSetBlendState 的文档,传nullptr会恢复到默认混合状态——也就是不透明的直接覆盖模式。

第二步——理解透明物体的渲染顺序问题

混合状态准备好了,但如果直接拿来渲染,你很可能会遇到一个非常诡异的现象:从某些角度看,透明物体遮挡了后面的不透明物体;换一个角度,该被透明物体遮挡的东西反而跑到了前面。

这个问题的根源在于渲染顺序。在不透明渲染中,深度缓冲完美地解决了遮挡问题——不管你按什么顺序画,近的总是覆盖远的。但 Alpha 混合不一样——它依赖目标颜色(渲染目标上已有的颜色)来计算最终结果。如果你先画了前面的透明物体,渲染目标上还没有后面的不透明物体的颜色,混合的结果就是透明物体和清屏颜色(通常是黑色或深蓝色)的混合,而不是和背景物体的混合。

所以透明渲染有一个铁律:先画所有不透明物体,然后从远到近(Back-to-Front)画所有透明物体

具体来说,正确的渲染流程是这样的:

  1. 开启深度测试和深度写入,正常渲染所有不透明物体。
  2. 对所有透明物体按距离摄像机的远近排序,远的排前面。
  3. 开启混合,关闭深度写入(但保持深度测试开启),按排序后的顺序逐个渲染透明物体。

为什么要关闭深度写入?因为如果透明物体写入了深度值,那么排在它后面的透明物体(虽然也应该是半透明的)在深度测试时会被判定为"被遮挡"而丢弃,结果就是你看不到后面的透明物体了。关闭深度写入确保了所有透明物体都能参与混合,但保持深度测试开启确保了透明物体仍然会被不透明物体正确遮挡。

在代码中,这需要为透明物体创建一个单独的深度模板状态:

// 透明物体使用的深度状态:测试开启,写入关闭D3D11_DEPTH_STENCIL_DESC transparentDepthDesc={};transparentDepthDesc.DepthEnable=TRUE;transparentDepthDesc.DepthWriteMask=D3D11_DEPTH_WRITE_MASK_ZERO;// 禁止深度写入transparentDepthDesc.DepthFunc=D3D11_COMPARISON_LESS;ComPtr<ID3D11DepthStencilState>pTransparentDepthState;g_pDevice->CreateDepthStencilState(&transparentDepthDesc,&pTransparentDepthState);

渲染时切换深度状态:

// 先画不透明物体(默认深度状态)RenderOpaqueObjects();// 切换到透明深度状态g_pContext->OMSetDepthStencilState(pTransparentDepthState.Get(),1);// 开启混合g_pContext->OMSetBlendState(pBlendState.Get(),blendFactor,0xFFFFFFFF);// 画透明物体(已排序)RenderTransparentObjects();// 恢复默认g_pContext->OMSetBlendState(nullptr,nullptr,0xFFFFFFFF);g_pContext->OMSetDepthStencilState(nullptr,1);

预乘 Alpha vs 直通 Alpha

在讨论 Alpha 混合时,有一个容易忽略但影响重大的概念:预乘 Alpha(Premultiplied Alpha)和直通 Alpha(Straight Alpha)。

直通 Alpha 是我们最直觉的理解方式——颜色是 RGB,透明度是独立的 Alpha 通道。一个半透明的红色像素可能是(1.0, 0.0, 0.0, 0.5),意思是"纯红色,50% 透明"。

预乘 Alpha 则要求 RGB 值预先乘以 Alpha 值。同一个半透明红色像素在预乘 Alpha 下应该是(0.5, 0.0, 0.0, 0.5)。预乘 Alpha 的混合公式变成了:

finalColor = srcColor + dstColor * (1 - srcAlpha)

注意源颜色不再乘以srcAlpha了——因为它已经被预先乘过了。

预乘 Alpha 有几个重要的好处。首先,它避免了"半透明边缘"的问题。当纹理被采样并缩放时,边缘像素的颜色是相邻像素的插值。如果使用直通 Alpha,一个完全不透明红色像素(1.0, 0.0, 0.0, 1.0)和一个完全透明像素(0.0, 0.0, 0.0, 0.0)的中间插值是(0.5, 0.0, 0.0, 0.5)——颜色和 Alpha 都是 0.5,混合时颜色会乘以 0.5 变成(0.25, 0.0, 0.0),边缘看起来会比预期暗。而预乘 Alpha 的插值结果是(0.5, 0.0, 0.0, 0.5),混合时直接加到目标上,边缘亮度正确。

其次,预乘 Alpha 允许"加法混合"和"透明混合"用同一套混合参数,不需要在运行时切换混合状态。这在粒子系统中特别有用——火焰粒子的中心可以是完全不透明但边缘逐渐透明,发光粒子的 RGB 值甚至可以超过 Alpha 值,产生"叠加发光"效果。

如果要用预乘 Alpha,混合状态需要调整为:

blendDesc.RenderTarget[0].SrcBlend=D3D11_BLEND_ONE;// 源已经预乘,不再乘AlphablendDesc.RenderTarget[0].DestBlend=D3D11_BLEND_INV_SRC_ALPHA;blendDesc.RenderTarget[0].BlendOp=D3D11_BLEND_OP_ADD;

Shader 中也需要在输出前预乘:

float4 PS_Main(PSInput input) : SV_TARGET { float4 color = /* 采样纹理或计算颜色 */; color.rgb *= color.a; // 预乘Alpha return color; }

对于这个系列来说,我们先用标准的直通 Alpha 配置(SrcBlend = SRC_ALPHA),因为它的直觉更清晰。但当你将来做粒子系统或 2D 精灵渲染时,强烈建议切换到预乘 Alpha。

第三步——实现粒子系统

有了混合状态和渲染顺序的知识,我们现在来实现一个最简单的粒子系统——100 个半透明方块,每个有随机位置、随机速度和随机透明度,在 3D 空间中飘动。

定义粒子结构体

structParticle{XMFLOAT3 position;// 世界空间位置XMFLOAT3 velocity;// 运动速度XMFLOAT4 color;// RGBA颜色floatlife;// 剩余生命值(秒)floatmaxLife;// 初始生命值};constintPARTICLE_COUNT=100;Particle particles[PARTICLE_COUNT];

粒子初始化

voidInitParticles(){std::srand(42);// 固定种子,方便复现for(inti=0;i<PARTICLE_COUNT;i++){particles[i].position=XMFLOAT3((std::rand()%200-100)/20.0f,// [-5, 5](std::rand()%200-100)/20.0f,// [-5, 5](std::rand()%200-100)/20.0f);// [-5, 5]particles[i].velocity=XMFLOAT3((std::rand()%100-50)/100.0f,(std::rand()%100-50)/100.0f,(std::rand()%100-50)/100.0f);// 随机颜色,带透明度floatr=(std::rand()%256)/255.0f;floatg=(std::rand()%256)/255.0f;floatb=(std::rand()%256)/255.0f;floata=0.3f+(std::rand()%70)/100.0f;// [0.3, 1.0]particles[i].color=XMFLOAT4(r,g,b,a);particles[i].life=3.0f+(std::rand()%40)/10.0f;// [3, 7] 秒particles[i].maxLife=particles[i].life;}}

粒子更新

每帧更新粒子位置和生命值,生命值耗尽后重生:

voidUpdateParticles(floatdt){for(inti=0;i<PARTICLE_COUNT;i++){// 更新位置particles[i].position.x+=particles[i].velocity.x*dt;particles[i].position.y+=particles[i].velocity.y*dt;particles[i].position.z+=particles[i].velocity.z*dt;// 减少生命值particles[i].life-=dt;// 生命结束时重生if(particles[i].life<=0.0f){particles[i].position=XMFLOAT3((std::rand()%200-100)/20.0f,(std::rand()%200-100)/20.0f,(std::rand()%200-100)/20.0f);particles[i].velocity=XMFLOAT3((std::rand()%100-50)/100.0f,(std::rand()%100-50)/100.0f,(std::rand()%100-50)/100.0f);floata=0.3f+(std::rand()%70)/100.0f;particles[i].color.w=a;particles[i].life=3.0f+(std::rand()%40)/10.0f;particles[i].maxLife=particles[i].life;}// 根据生命值调整透明度(淡出效果)floatlifeRatio=particles[i].life/particles[i].maxLife;particles[i].color.w*=lifeRatio;}}

按距离排序

透明渲染的关键步骤——将粒子按距离摄像机的远近从远到近排序:

voidSortParticlesBackToFront(constXMFLOAT3&cameraPos){std::sort(particles,particles+PARTICLE_COUNT,[&cameraPos](constParticle&a,constParticle&b){XMVECTOR camVec=XMLoadFloat3(&cameraPos);XMVECTOR vecA=XMLoadFloat3(&a.position);floatdistA=XMVectorGetX(XMVector3LengthSq(vecA-camVec));XMVECTOR vecB=XMLoadFloat3(&b.position);floatdistB=XMVectorGetX(XMVector3LengthSq(vecB-camVec));returndistA>distB;// 远的排在前面});}

这里用距离的平方(XMVector3LengthSq)而不是距离本身来比较,避免开方运算。因为我们只需要比较远近,不需要精确距离,距离平方的比较结果和距离完全一致。

渲染粒子

每个粒子用一个小的 billboard 四边形来渲染。为了让粒子始终面向摄像机,我们需要在 C++ 端根据摄像机方向计算粒子的四个顶点位置:

voidRenderParticles(){XMFLOAT3 cameraPos=XMFLOAT3(0,2,-5);// 与光照文章一致的摄像机位置SortParticlesBackToFront(cameraPos);// 切换到透明深度状态g_pContext->OMSetDepthStencilState(g_pTransparentDepthState.Get(),1);// 开启混合floatblendFactor[4]={0.0f,0.0f,0.0f,0.0f};g_pContext->OMSetBlendState(g_pBlendState.Get(),blendFactor,0xFFFFFFFF);// 为每个粒子生成billboard四边形并渲染for(inti=0;i<PARTICLE_COUNT;i++){// 更新CBuffer中的世界矩阵和颜色XMMATRIX world=XMMatrixTranslationFromVector(XMLoadFloat3(&particles[i].position));// 简化处理:粒子不旋转,只是平移一个小方块world*=XMMatrixScaling(0.2f,0.2f,0.2f);PerFrameCB cb={};XMStoreFloat4x4(&cb.worldViewProj,XMMatrixTranspose(world*g_viewProj));XMStoreFloat4x4(&cb.world,XMMatrixTranspose(world));// 粒子颜色直接作为材质颜色cb.particleColor=particles[i].color;D3D11_MAPPED_SUBRESOURCE ms;g_pContext->Map(g_pCB,0,D3D11_MAP_WRITE_DISCARD,0,&ms);memcpy(ms.pData,&cb,sizeof(PerFrameCB));g_pContext->Unmap(g_pCB,0);// 绘制粒子四边形g_pContext->Draw(4,0);// 4个顶点}// 恢复默认状态g_pContext->OMSetBlendState(nullptr,nullptr,0xFFFFFFFF);g_pContext->OMSetDepthStencilState(nullptr,1);}

⚠️ 注意这里有一个性能隐患——100 个粒子意味着 100 次Draw调用和 100 次 CBuffer 更新。在实际项目中,你会用实例化(Instancing)或者动态顶点缓冲来把所有粒子合并到一次 Draw Call 中。但对于学习目的,逐粒子绘制更直观,也更容易理解每一步在做什么。

第四步——粒子 Shader

粒子使用的 Shader 和之前的光照 Shader 有所不同——粒子不需要光照计算,只需要输出带 Alpha 的颜色:

cbuffer PerFrameCB : register(b0) { matrix g_WorldViewProj; matrix g_World; float4 g_ParticleColor; // 粒子颜色(含Alpha) }; struct PSInput { float4 position : SV_POSITION; float4 color : COLOR; }; PSInput VS_Main(float3 pos : POSITION) { PSInput output; output.position = mul(float4(pos, 1.0f), g_WorldViewProj); output.color = g_ParticleColor; return output; } float4 PS_Main(PSInput input) : SV_TARGET { // 直接输出颜色,Alpha通道由混合阶段处理 return input.color; }

Shader 非常简单——顶点着色器只做 MVP 变换,像素着色器直接输出颜色。Alpha 混合的"混合"操作不在 Shader 中发生,而是在管线的 Output Merger 阶段由硬件自动执行。这也是为什么混合不需要在 Shader 中写任何特殊代码的原因——你只需要确保输出的颜色包含正确的 Alpha 值,混合状态会完成剩下的工作。

完整的渲染循环

把所有部分组合起来,完整的渲染循环如下:

voidRenderFrame(floatdt){// 更新粒子UpdateParticles(dt);// 清屏floatclearColor[4]={0.1f,0.1f,0.2f,1.0f};g_pContext->ClearRenderTargetView(g_pRTV,clearColor);g_pContext->ClearDepthStencilView(g_pDSV,D3D11_CLEAR_DEPTH,1.0f,0);// ========== 第1步:渲染不透明物体 ==========g_pContext->OMSetBlendState(nullptr,nullptr,0xFFFFFFFF);g_pContext->OMSetDepthStencilState(nullptr,1);RenderOpaqueObjects();// 渲染立方体等不透明物体// ========== 第2步:渲染透明粒子 ==========RenderParticles();// Presentg_pSwapChain->Present(1,0);}

渲染循环中最重要的结构就是"先不透明,后透明"的两阶段划分。不管你的场景多复杂,这个顺序都是必须的——所有不透明物体必须在透明物体之前渲染完毕。

⚠️ 踩坑预警

透明渲染有几个经典的坑,每一个都能让你调试半天。

坑点一:忘记关闭透明物体的深度写入

这是最常见也最让人困惑的问题。如果你渲染透明物体时仍然开启了深度写入,那么第一个被渲染的透明粒子会写入深度值,后续所有在这个粒子后面的透明粒子在深度测试时都会失败,结果就是你只能看到最前面的一层粒子——不管它有多透明。这在调试时看起来就像是"粒子消失了",让人怀疑是不是混合状态没生效,但实际上是深度写入在捣鬼。

坑点二:渲染 UI 叠加层时忘记关闭深度测试

当你在 3D 场景上叠加一个 2D 的 UI 层(比如准星、血条、文字)时,如果 UI 的绘制仍然开启了深度测试,UI 可能会被场景中的 3D 物体遮挡。解决方案是在绘制 UI 之前关闭深度测试(DepthEnable = FALSE),或者将 UI 的 Z 坐标设为最近的值。

坑点三:排序错误导致视觉伪影

Back-to-Front 排序在粒子数量大时(几千个粒子)会带来明显的 CPU 开销。如果你偷懒不排序,粒子之间的混合顺序就是不确定的——从不同角度看效果不一样。一种常见的优化是使用加法混合(SrcBlend = ONE, DestBlend = ONE),加法混合不依赖顺序,不需要排序,但它只适合"发光"类型的粒子(火焰、能量效果),不适合需要"半透明遮挡"的场景。

坑点四:混合状态未恢复导致后续渲染异常

如果你在渲染透明物体后忘了恢复混合状态,后续的不透明物体会继续使用透明混合——结果是所有东西都变透明了。养成习惯:每次OMSetBlendState修改混合状态后,在渲染结束后恢复默认。

常见问题与调试

混合后颜色变暗

检查你的混合参数配置。如果你用了预乘 Alpha 的混合参数(SrcBlend = ONE)但 Shader 输出的颜色没有预乘 Alpha,结果就是颜色比预期亮。反过来,如果你用了直通 Alpha 的混合参数(SrcBlend = SRC_ALPHA)但 Shader 已经预乘了 Alpha,颜色就会比预期暗。保持混合参数和 Shader 逻辑的一致性。

粒子边缘有锯齿

如果你的粒子使用点精灵(Point Sprite)或者简单的方形,边缘会有明显的锯齿。解决方法是在 Shader 中做圆形裁剪:根据片元到中心的距离来计算 Alpha,越靠近边缘 Alpha 越小,实现平滑过渡。

大量粒子时排序开销过高

对于上千个粒子,CPU 端的排序可能成为瓶颈。可以考虑以下优化方案:使用 GPU 排序(Compute Shader 中的 Bitonic Sort 或 Radix Sort)、使用与顺序无关的透明度算法(OIT,如 Depth Peeling 或 Weighted Blended OIT)、或者用加法混合完全避免排序。

总结

这一篇我们拆解了 D3D11 的 Alpha 混合机制。

从数学上看,混合就是src * srcAlpha + dst * (1 - srcAlpha)这个简单的线性插值公式。但要让它在实际渲染中正确工作,需要配合正确的渲染顺序(先不透明后透明、透明物体从远到近)和正确的深度状态(透明物体关闭深度写入)。我们创建了一个标准的 Alpha 混合状态(D3D11_BLEND_DESC),实现了一个 100 个粒子的简单粒子系统,并且在实践中遇到了透明渲染最典型的几个坑。

我们还讨论了预乘 Alpha 和直通 Alpha 的区别——预乘 Alpha 在边缘插值质量和混合状态灵活性上都有优势,是实际项目中的推荐做法。

到这里,我们的 D3D11 之旅已经覆盖了渲染管线的核心部分——初始化、顶点输入、纹理采样、深度缓冲、光照模型和混合透明。下一篇开始,我们要跨入一个全新的领域:Direct3D 12。这不是一次简单的版本升级,而是一场设计哲学的根本性变革。


练习

  1. 在光照模型的立方体场景中添加 100 个半透明粒子,粒子从立方体周围随机位置飘出,带透明度,按距离排序渲染。观察粒子和不透明立方体之间的遮挡关系是否正确。

  2. 修改粒子的混合模式为加法混合(SrcBlend = D3D11_BLEND_ONE, DestBlend = D3D11_BLEND_ONE),对比加法混合和标准 Alpha 混合的视觉效果差异。思考为什么加法混合不需要排序。

  3. 在 Shader 中为粒子实现圆形裁剪:将粒子从方形变成圆形,边缘平滑过渡(根据片元到中心的距离计算 Alpha)。提示:使用discard或者将 Alpha 乘以距离衰减因子。

  4. 尝试使用预乘 Alpha:修改混合参数为SrcBlend = ONE,同时在 Shader 中预乘颜色(color.rgb *= color.a),对比标准 Alpha 混合和预乘 Alpha 在粒子边缘的表现差异。


参考资料:

  • D3D11_BLEND_DESC structure - Microsoft Learn
  • ID3D11DeviceContext::OMSetBlendState - Microsoft Learn
  • ID3D11BlendState interface - Microsoft Learn
  • D3D11_BLEND enumeration - Microsoft Learn
  • Premultiplied Alpha - Shawn Hargreaves’ blog
  • Output Merger Stage - Microsoft Learn
  • Transparency Sorting - Microsoft Learn

相关阅读

  1. 现代Qt开发教程(新手篇)1.1——QObject 与元对象系统 - 相似度 100%
  2. 现代Qt开发教程(新手篇)1.2——信号与槽 - 相似度 100%
  3. 通用GUI编程技术——图形渲染实战(二十八)——图像格式与编解码:PNG/JPEG全掌握 - 相似度 100%
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 18:09:31

ChatGPT在Kattis算法题实测:AI编程的强项与局限分析

1. 项目缘起&#xff1a;当AI解题遇上“铁面判官” 最近几个月&#xff0c;我身边不少搞算法竞赛和刷题的朋友都在讨论一个话题&#xff1a;ChatGPT这类大语言模型&#xff0c;到底能不能用来“刷题”&#xff1f;它能理解复杂的算法问题描述吗&#xff1f;能写出能通过严格测试…

作者头像 李华
网站建设 2026/5/9 18:08:50

ACAI平台:基于数据湖与智能调度的MLOps实验管理实践

1. 项目概述&#xff1a;当MLOps遇上数据湖与智能调度在机器学习&#xff08;ML&#xff09;项目从研究走向生产的漫长征途中&#xff0c;实验管理一直是个让人又爱又恨的环节。爱的是&#xff0c;每一次实验都可能是通往更高模型性能的钥匙&#xff1b;恨的是&#xff0c;随着…

作者头像 李华
网站建设 2026/5/9 18:08:09

AMCT模型压缩工具包

AGENTS.md 【免费下载链接】amct AMCT是CANN提供的昇腾AI处理器亲和的模型压缩工具仓。 项目地址: https://gitcode.com/cann/amct 本文件为 agent 在此代码仓库中工作时提供指导。 项目概述 AMCT&#xff08;Ascend Model Compression Toolkit&#xff09;是一款昇腾…

作者头像 李华
网站建设 2026/5/9 18:08:03

CANN/ge源码构建指南

源码构建 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的…

作者头像 李华
网站建设 2026/5/9 17:58:57

Taotoken模型广场如何帮助开发者快速选型与切换合适的大模型

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken模型广场如何帮助开发者快速选型与切换合适的大模型 对于需要接入大模型能力的开发者而言&#xff0c;面对市场上众多的模…

作者头像 李华