共享纹理(1)- Spout2 小试牛刀

1.下载Spout2 源码

git clone https://github.com/leadedge/Spout2.git

2. 开始编译

3C74681C-DE64-4dad-8CBF-7270D1969994.png
  • 如果需要DirectX, 需要开启 SPOUT_BUILD_SPOUTD
  • Configure, Generate 就能得到 Visual Studio 2022 项目;
  • Open Project 打开项目

3. 生成动态库

A30EB1A9-2B71-42ee-9616-B17D906BD67B.png
  • 选择需要的系统架构,生成对应产物

4. 创建c++ 控制台项目,引入动态库

4.1 复制产物到自己的工程

  • third_party\Spout2\bin\x64
    CE8C754F-A5B6-417b-AE16-24A6E936024B.png
  • third_party\Spout2\lib\x64
    817196C6-36CB-427c-A032-8577F3D961B1.png
  • third_party\Spout2\include
    8289410D-299E-412d-8DC3-6AEB8D58560A.png

4.2 配置工程引入动态库

  • $(ProjectDir)third_party\Spout2\include

    9DDED3F6-E384-4d61-A189-DDCE731038FB.png

  • $(ProjectDir)third_party\Spout2\lib\x64

    8B78C5E4-478F-44fe-A625-1881C9A394B1.png

  • xcopy /y /d /s /e "$(ProjectDir)third_party\Spout2\bin\x64\*.*" "$(TargetDir)"

64DB2943-2710-4b9d-809E-757695CA412A.png

5. 修改主文件

#include <iostream>
#include <windows.h>
#include <vector>
#include <ctime>
#include <iomanip>
#include <sstream>

// 1. 引入纯 DX11 专门类头文件
#include "SpoutDX.h" 

// 2. 核心桥梁:强制让链接器去寻找微软原生 D3D11 图形库
#pragma comment(lib, "d3d11.lib")

// 3. 链接你编译出的全功能库
#pragma comment(lib, "SpoutDX.lib")
#pragma comment(lib, "Spout.lib")

// =================================================================
// 🔑 极简 8x16 点阵字库:支持数字 '0'-'9' 和冒号 ':'
// 每一个字节代表一行,1代表有像素(白色),0代表无像素(透明)
// =================================================================
const unsigned char Font8x16[11][16] = {
    {0x00,0x3c,0x46,0x46,0x4e,0x56,0x66,0x66,0x66,0x66,0x6c,0x64,0x64,0x3c,0x00,0x00}, // '0'
    {0x00,0x18,0x1c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00}, // '1'
    {0x00,0x3c,0x46,0x46,0x46,0x06,0x0c,0x18,0x30,0x60,0x40,0x42,0x42,0x7e,0x00,0x00}, // '2'
    {0x00,0x3c,0x46,0x46,0x06,0x06,0x1c,0x06,0x06,0x06,0x06,0x46,0x46,0x3c,0x00,0x00}, // '3'
    {0x00,0x0c,0x1c,0x3c,0x2c,0x5c,0x4c,0x8c,0x8c,0xff,0x0c,0x0c,0x0c,0x1e,0x00,0x00}, // '4'
    {0x00,0x7e,0x40,0x40,0x40,0x7c,0x46,0x06,0x06,0x06,0x06,0x46,0x46,0x3c,0x00,0x00}, // '5'
    {0x00,0x1c,0x30,0x60,0x60,0x7c,0x66,0x66,0x66,0x66,0x66,0x66,0x3c,0x18,0x00,0x00}, // '6'
    {0x00,0x7e,0x46,0x46,0x06,0x0c,0x0c,0x18,0x18,0x30,0x30,0x30,0x30,0x30,0x00,0x00}, // '7'
    {0x00,0x3c,0x66,0x66,0x66,0x3c,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3c,0x00,0x00}, // '8'
    {0x00,0x18,0x3c,0x66,0x66,0x66,0x66,0x66,0x3e,0x06,0x06,0x0c,0x18,0x38,0x00,0x00}, // '9'
    {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00}  // ':'
};

// 根据字符获取字库索引
int GetFontIndex(char c) {
    if (c >= '0' && c <= '9') return c - '0';
    if (c == ':') return 10;
    return -1;
}

// 🔑 在内存像素缓冲区上绘制单个 8x16 字符的函数 (带像素放大系数 scale,让字变大)
void DrawChar(std::vector<unsigned char>& buffer, unsigned int bufW, unsigned int bufH,
    int startX, int startY, char c, unsigned char r, unsigned char g, unsigned char b, int scale = 4) {
    int fontIdx = GetFontIndex(c);
    if (fontIdx == -1) return;

    // 遍历 16 行
    for (int row = 0; row < 16; ++row) {
        unsigned char rowData = Font8x16[fontIdx][row];
        // 遍历 8 列 (从高位到低位检测二进制位)
        for (int col = 0; col < 8; ++col) {
            if ((rowData >> (7 - col)) & 0x01) {
                // 1 表示有像素,根据 scale 放大系数,填充一个矩形区域
                for (int sy = 0; sy < scale; ++sy) {
                    for (int sx = 0; sx < scale; ++sx) {
                        unsigned int pixelX = startX + col * scale + sx;
                        unsigned int pixelY = startY + row * scale + sy;

                        // 边界检查,防止内存越界
                        if (pixelX < bufW && pixelY < bufH) {
                            unsigned int idx = (pixelY * bufW + pixelX) * 4;
                            buffer[idx + 0] = r; // R
                            buffer[idx + 1] = g; // G
                            buffer[idx + 2] = b; // B
                            buffer[idx + 3] = 255; // A (不透明)
                        }
                    }
                }
            }
        }
    }
}

// 🔑 在内存像素缓冲区上绘制整串字符串
void DrawString(std::vector<unsigned char>& buffer, unsigned int bufW, unsigned int bufH,
    int startX, int startY, const std::string& str, unsigned char r, unsigned char g, unsigned char b, int scale = 4) {
    int currentX = startX;
    for (char c : str) {
        DrawChar(buffer, bufW, bufH, currentX, startY, c, r, g, b, scale);
        currentX += 8 * scale + 2; // 每个字符横向右移,2为字间距
    }
}

int main() {
    std::cout << "===================================================" << std::endl;
    std::cout << "===  Spout2 纯 DX11 裸像素投递 (包含实时系统时间) ===" << std::endl;
    std::cout << "===================================================" << std::endl;

    spoutDX sender;

    if (!sender.OpenDirectX11()) {
        std::cout << "【错误】spoutDX 初始化 DX11 环境失败!" << std::endl;
        return -1;
    }

    const char* streamName = "My_DX11_Pixel_Stream";
    sender.SetSenderName(streamName);

    unsigned int width = 1920;
    unsigned int height = 1080;

    std::cout << "【成功】spoutDX 广播全线就绪!通道名: " << streamName << std::endl;

    // 开辟 1080P RGBA 像素内存
    std::vector<unsigned char> pixelBuffer(width * height * 4, 0);

    std::cout << "\n>>> 正在实时刷新时间并投递画面,请在 OBS 里观察..." << std::endl;
    std::cout << ">>> 提示:在当前窗口按下 [ESC] 键可安全退出程序。\n" << std::endl;

    unsigned char colorOffset = 0;

    // 主循环
    while (true) {
        colorOffset += 1; // 背景渐变速度

        // 1. 渲染背景底色(随时间渐变的深色流体背景,方便看清白色的时间字)
        for (unsigned int i = 0; i < width * height; ++i) {
            unsigned int idx = i * 4;
            pixelBuffer[idx + 0] = colorOffset;        // R
            pixelBuffer[idx + 1] = 40;                 // G
            pixelBuffer[idx + 2] = 90;                 // B
            pixelBuffer[idx + 3] = 255;                // A
        }

        // 2. 获取当前 Windows 系统的高精度本地时间
        std::time_t t = std::time(nullptr);
        std::tm now;
        localtime_s(&now, &t);

        // 格式化时间字符串为 "HH:MM:SS"
        std::ostringstream timeStream;
        timeStream << std::setfill('0')
            << std::setw(2) << now.tm_hour << ":"
            << std::setw(2) << now.tm_min << ":"
            << std::setw(2) << now.tm_sec;
        std::string timeStr = timeStream.str();

        // 🔑 3. 核心灌入:把时间文字直接画到像素内存缓冲区中
        // 参数:像素矩阵, 宽, 高, 起始X(100), 起始Y(100), 时间字符串, 颜色R(255), G(255), B(255), 放大倍数(5倍,让字很大很清晰)
        DrawString(pixelBuffer, width, height, 100, 100, timeStr, 255, 255, 255, 5);

        // 4. 清理 Alpha 并发送图像
        sender.spoutcopy.ClearAlpha(pixelBuffer.data(), width, height, 255);
        sender.SendImage(pixelBuffer.data(), width, height);

        // 5. 帧率锁死 60 帧
        sender.HoldFps(60);

        if (GetAsyncKeyState(VK_ESCAPE)) break;
    }

    std::cout << "正在关闭 spoutDX 通道..." << std::endl;
    sender.ReleaseSender();
    sender.CloseDirectX11();

    std::cout << "程序已安全退出。" << std::endl;
    return 0;
}

6. 使用obs 添加我们共享的纹理

59F49A38-5720-4621-9399-807165A2AC62.png
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容