跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

Direct3D 融合技术与透明效果实现

综述由AI生成Direct3D 融合技术利用混合方程将源像素与目标像素合成,常用于实现透明效果。核心在于深度排序与融合因子的配置,如使用 SRCALPHA 和 INVSRCALPHA。文中提供了完整的 D3D9 初始化及渲染状态设置示例,展示了如何启用 Alpha 混合并处理纹理通道。实际应用中需注意绘制顺序及混合状态的及时关闭,以避免视觉错误。

雾岛听风发布于 2025/1/19更新于 2026/6/1223 浏览
Direct3D 融合技术与透明效果实现

融合技术概述

融合(Blending)技术允许我们将当前正在光栅化的像素颜色与后台缓存中已存在的同位置像素进行合成。简单来说,就是把图元颜色值与帧缓冲区中的现有颜色混合。这项技术是实现透明、半透明以及复杂光影效果的基础。

核心方程与绘制顺序

在融合运算时,必须明确一个原则:当前处理的三角形单元是与已写入后台缓存的像素进行交互。因此,为了获得正确的视觉效果,绘制顺序至关重要。

最佳实践是:

  1. 先绘制不需要融合的物体。
  2. 将需要融合的物体按照相对于摄像机的深度值排序。
  3. 如果物体已在观察坐标系中,只需对 Z 分量排序即可,效率较高。
  4. 按照自后往前的顺序逐个绘制待融合物体。

两个像素颜色值进行融合的标准公式如下:

OutputPixel = SourcePixel * SourceBlendFactor + DestPixel * DestBlendFactor

这里的每个变量都是一个 4D 颜色向量 (r, g, b, a),符号 * 表示分量逐个相乘。各参数含义如下:

  • OutputPixel: 融合后的最终颜色值。
  • SourcePixel: 当前计算得到的源像素颜色值。
  • SourceBlendFactor: 源融合因子,决定源像素在结果中的权重,取值范围 [0, 1]。
  • DestPixel: 后台缓存中现有的目标像素颜色值。
  • DestBlendFactor: 目标融合因子,决定目标像素在结果中的权重,取值范围 [0, 1]。

需要注意的是,默认状态下融合运算是被禁止的。由于融合计算开销较大,建议仅在必要时开启。绘制完需要融合的几何体后应立即关闭 Alpha 融合。对于三角形组,最好进行批处理后再统一绘制,避免每帧都频繁启用和禁用融合状态。

融合因子设置

通过调整融合方程中的因子,可以创造出多种不同的视觉效果。在 Direct3D 中,我们通过设置渲染状态 D3DRS_SRCBLEND 和 D3DRS_DESTBLEND 来控制源和目标融合因子。

Device->SetRenderState(D3DRS_SRCBLEND, Source);
Device->SetRenderState(D3DRS_DESTBLEND, Destination);

常用的融合因子包括:

  • D3DBLEND_ZERO: 零
  • D3DBLEND_ONE: 一
  • D3DBLEND_SRCCOLOR: 源颜色
  • D3DBLEND_INVSRCCOLOR: 反色源颜色
  • D3DBLEND_SRCALPHA: 源 Alpha
  • D3DBLEND_INVSRCALPHA: 反色源 Alpha
  • D3DBLEND_DESTALPHA: 目标 Alpha
  • D3DBLEND_INVDESTALPHA: 反色目标 Alpha
  • D3DBLEND_DESTCOLOR: 目标颜色
  • D3DBLEND_INVDESTCOLOR: 反色目标颜色
  • D3DBLEND_SRCALPHASAT: 源 Alpha 饱和
  • D3DBLEND_BOTHSRCALPHA: 双源 Alpha
  • D3DBLEND_BOTHINVSRCALPHA: 双反源 Alpha
  • D3DBLEND_BLENDFACTOR: 混合因子
  • : 反混合因子
D3DBLEND_INVBLENDFACTOR

透明度与 Alpha 通道

在融合中,顶点颜色的 Alpha 分量和材质属性是关键。顶点的 Alpha 分量沿三角形表面渐变,它不直接决定颜色,而是决定透明度。

假设 Alpha 分量占用 8 位,合法区间为 [0, 255],对应透明度 [0%, 100%]。Alpha 值为 0 表示完全透明,255 表示完全不透明。

要利用 Alpha 描述透明度,通常将源融合因子设为 D3DBLEND_SRCALPHA,目标融合因子设为 D3DBLEND_INVSRCALPHA。这恰好也是 Direct3D 的默认配置。

Alpha 来源

我们通常不从计算中得到 Alpha,而是直接从纹理的 Alpha 通道读取。当纹理映射到图元时,Alpha 通道信息也随之映射成为像素的 Alpha 分量。

默认情况下,如果纹理有 Alpha 通道,Alpha 值取自该通道;否则取自顶点颜色。也可以通过以下状态显式指定:

// 从漫反射颜色计算 Alpha
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

// 从纹理 Alpha 通道获取
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

实战代码示例

下面是一个完整的 D3D9 初始化及渲染流程示例,展示了如何设置融合状态并绘制带透明度的纹理。

#include <Windows.h>
#include <mmsystem.h>
#include <d3dx9.h>
#pragma warning( disable : 4996 )
#include <strsafe.h>
#pragma warning( default : 4996 )
#include <d3dx9math.h>

LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
LPDIRECT3DTEXTURE9 g_pTexture = NULL;

struct CUSTOMVERTEX {
    FLOAT x, y, z;
    FLOAT tu, tv;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1)

HRESULT InitD3D(HWND hWnd) {
    if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice))) {
        return E_FAIL;
    }
    return S_OK;
}

HRESULT InitGeometry() {
    if (FAILED(D3DXCreateTextureFromFile(g_pd3dDevice, L"cratealpha.dds", &g_pTexture))) {
        return E_FAIL;
    }

    CUSTOMVERTEX vertices[] = {
        { -10.0f, -10.0f, 0.0f, 0, 1 },
        { -10.0f, 10.0f, 0.0f, 0, 0 },
        { 10.0f, 10.0f, 0.0f, 1, 0 },
        { 10.0f, -10.0f, 0.0f, 1, 1 },
    };

    if (FAILED(g_pd3dDevice->CreateVertexBuffer(sizeof(vertices) / sizeof(vertices[0]) * sizeof(CUSTOMVERTEX),
        0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL))) {
        return E_FAIL;
    }

    VOID* pVertices;
    if (FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0))) return E_FAIL;
    memcpy(pVertices, (void*)vertices, sizeof(vertices));
    g_pVB->Unlock();

    WORD indices[] = { 0, 1, 2, 0, 2, 3 };
    if (FAILED(g_pd3dDevice->CreateIndexBuffer(sizeof(indices) / sizeof(indices[0]) * sizeof(WORD),
        0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIB, NULL))) {
        return E_FAIL;
    }

    VOID* pIndices = NULL;
    if (FAILED(g_pIB->Lock(0, sizeof(indices), (void**)&pIndices, 0))) return E_FAIL;
    memcpy(pIndices, (VOID*)indices, sizeof(indices));
    g_pIB->Unlock();

    return S_OK;
}

VOID SetupMatrices() {
    D3DXVECTOR3 vEyePt(0.0f, 0.0f, -30);
    D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);

    D3DXMATRIXA16 matView;
    D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
    g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);

    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 200.0f);
    g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}

VOID Cleanup() {
    if (g_pTexture != NULL) g_pTexture->Release();
    if (g_pVB != NULL) g_pVB->Release();
    if (g_pIB != NULL) g_pIB->Release();
    if (g_pd3dDevice != NULL) g_pd3dDevice->Release();
    if (g_pD3D != NULL) g_pD3D->Release();
}

VOID Render() {
    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
        D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);

    if (SUCCEEDED(g_pd3dDevice->BeginScene())) {
        SetupMatrices();
        g_pd3dDevice->SetTexture(0, g_pTexture);
        g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
        g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
        g_pd3dDevice->SetIndices(g_pIB);
        g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

        // 关键步骤:设置 Alpha 来源为纹理通道
        g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
        g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

        // 关键步骤:启用 Alpha 混合
        g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
        g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
        g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

        g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 4, 0, 2);

        // 绘制完成后立即关闭混合,避免影响后续不透明物体
        g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

        g_pd3dDevice->EndScene();
    }
    g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_DESTROY:
        Cleanup();
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT) {
    UNREFERENCED_PARAMETER(hInst);
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
        GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"D3D Tutorial", NULL };
    RegisterClassEx(&wc);

    HWND hWnd = CreateWindow(L"D3D Tutorial", L"D3D Tutorial 03: CreateAlpha",
        WS_OVERLAPPEDWINDOW, 100, 100, 700, 700, NULL, NULL, wc.hInstance, NULL);

    if (SUCCEEDED(InitD3D(hWnd))) {
        if (SUCCEEDED(InitGeometry())) {
            ShowWindow(hWnd, SW_SHOWDEFAULT);
            UpdateWindow(hWnd);

            MSG msg;
            ZeroMemory(&msg, sizeof(msg));
            while (msg.message != WM_QUIT) {
                if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                } else Render();
            }
        }
    }
    UnregisterClass(L"D3D Tutorial", wc.hInstance);
    return 0;
}

这段代码演示了如何初始化设备、创建几何体,并在渲染循环中正确配置混合状态。注意在绘制透明物体后,我立即将 ALPHABLENDENABLE 设回 FALSE,这是为了防止后续的不透明物体受到残留的混合状态影响,这是一个容易忽略但至关重要的细节。

目录

  1. 融合技术概述
  2. 核心方程与绘制顺序
  3. 融合因子设置
  4. 透明度与 Alpha 通道
  5. Alpha 来源
  6. 实战代码示例
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 大模型在各国政务领域应用深度研究报告
  • PX4 开源飞控系统概述及入门指南
  • 基于 KaiwuDB 的 PX4-ROS2 无人机仿真时序数据存储与分析实践
  • MixAIHub 镜像站使用指南:快速访问主流 AI 模型
  • Python 核心技术指南:语法、数据结构、算法与框架解析
  • Claude Code、OpenClaw、OpenCode 与 SkillLite 架构深度对比
  • C++ 继承机制详解:从基础语法到多态应用
  • OpenClaw 本地部署指南:隧道配置与百炼模型接入
  • 前端实战:如何实现用户上次阅读位置恢复
  • AR 交互设计新范式:虚拟按钮与手势控制在教育类应用中的创新融合
  • HarmonyOS RcList 组件事件处理机制与应用示例
  • AI Agent 实战:核心架构与生产级落地指南
  • AI Agent 框架选型实战:OpenClaw、LangChain、AutoGPT 与 CrewAI 对比
  • C++ String 赋值运算符重载:从“砸碗”到“换碗仪式”
  • 绿联 NAS 配置 WebDAV 公网访问并使用 RaiDrive 挂载
  • 本地 Docker 部署 Appsmith 及远程访问配置指南
  • LangChain Tool 异常处理实战
  • Claude Code 平替方案:OpenCode 搭配 GitHub Copilot 使用指南
  • 语义化 AI 驱动器:提示词工程的技术演进与未来展望
  • C++ 性能分析工具全景与选型指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online