行业资讯

Unreal Engine像素流送技术:实现网页端实时交互3D渲染

发布时间:2026/7/27 16:53:59
Unreal Engine像素流送技术:实现网页端实时交互3D渲染 最近在做一个需要将 Unreal Engine 渲染的 3D 场景实时展示在网页上的项目遇到了一个核心难题如何将 UE 庞大的程序“轻量化”地送到浏览器里并且还能让网页和 UE 程序“对话”经过一番探索和实践最终选择了 UE 的“像素流送”技术它完美地解决了这个问题。本文将为你完整拆解从 UE 项目配置、信令服务器搭建到前端网页开发的全流程并实现两者之间的双向通信。无论你是想将 UE 应用部署到云端还是想在网页中嵌入一个可交互的 3D 可视化界面这套方案都能直接复用。1. 背景与核心概念为什么需要像素流送在传统的 Web 3D 方案中我们通常使用 Three.js、Babylon.js 等库在浏览器端直接渲染。但对于由 Unreal Engine 这类重型游戏引擎制作的、包含复杂光照、材质、物理和特效的高保真场景浏览器的性能往往难以承受。像素流送就是为了解决这个矛盾而生的技术。它的核心思想是“计算与显示分离”计算端UE 应用程序在性能强大的服务器或本地高性能机器上运行完成所有复杂的渲染计算。显示端服务器将渲染出的每一帧画面实时编码成视频流如 H.264通过网络通常是 WebRTC推送到用户的网页浏览器中。交互端用户在网页中的鼠标、键盘等操作被实时传送回服务器端的 UE 程序驱动场景中的响应。简单来说网页变成了一个“远程桌面”客户端而 UE 程序在后台运行。对于用户而言就像在本地运行一个高清 3D 应用一样流畅。与常见方案的对比WebGL/WebGPU 本地渲染受限于浏览器性能和 UE 功能子集难以实现 UE 的全部效果。录制视频播放无法交互是单向的。云游戏/云渲染方案像素流送正是此类方案的技术基础之一但 UE 提供了官方、相对易用的实现。核心价值跨平台与零安装用户只需一个现代浏览器无需下载、安装几个 G 的客户端。硬件要求转移对用户终端硬件要求极低所有渲染压力都在服务器端。保护知识产权核心的 UE 项目逻辑和资源始终在服务器端不易被反编译或窃取。即时更新更新服务器端的 UE 应用所有用户刷新页面即可获得最新版本。2. 环境准备与版本说明在开始动手之前请确保你已准备好以下环境。版本差异可能导致配置步骤不同请务必注意。2.1 核心软件与版本Unreal Engine本文以UE 5.3为例。UE 4.27 及以上版本均支持像素流送但具体插件和配置项可能略有不同。请确保已成功安装 Epic Games Launcher 并下载对应版本的引擎。操作系统Windows 10/11 或 Linux如 Ubuntu 20.04。本文演示环境为 Windows。Node.js用于运行信令服务器和前端构建。推荐LTS 版本如 18.x, 20.x。Python部分 UE 插件脚本依赖 Python 3.7。通常 UE 安装包会自带。2.2 项目说明我们将创建一个标准的 UE 项目并启用像素流送插件。前端部分我们将使用一个简单的 HTML/JavaScript 页面来连接和交互。为了简化信令服务器我们将使用 Epic 官方提供的Node.js示例。最终项目结构预览PixelStreamingDemo/ ├── UE_Project/ # Unreal Engine 项目文件夹 │ ├── Content/ │ ├── Source/ │ ├── Config/ │ └── ... ├── SignalingServer/ # 信令服务器 │ ├── cirrus.js # 官方示例服务器脚本 │ └── package.json └── WebFrontend/ # 前端网页 ├── index.html ├── app.js ├── style.css └── ...3. 核心原理与组件拆解理解像素流送的架构是成功部署的关键。整个系统主要由三部分组成3.1 三大核心组件UE 应用程序 (Pixel Streaming Producer)运行在服务器上的 UE 可执行程序。集成Pixel Streaming插件负责捕获渲染帧通过NVENCN卡或AMFA卡等硬件编码器压缩为视频流。通过WebRTC协议将视频流、音频流推送出去并接收来自网页的控制指令流。信令服务器 (Signaling Server)这是一个Node.js或其他语言编写的 WebSocket 服务器。核心作用在 UE 应用和网页客户端之间建立WebRTC对等连接。它负责交换双方的网络信息SDP/ICE candidate相当于一个“媒人”。它不传输音视频数据数据走 P2P 的 WebRTC 通道只传输控制信令。前端网页 (Pixel Streaming Client)运行在用户浏览器中的 HTML/JavaScript 页面。包含一个video元素用于显示视频流。使用 Epic 提供的frontend.js库或自行基于WebRTC开发负责接收视频流、发送用户输入鼠标、键盘、触摸。3.2 通信流程用户访问前端网页。网页通过 WebSocket 连接到信令服务器并告知“我想看流”。UE 应用启动后也通过 WebSocket 连接到同一个信令服务器告知“我有流可发”。信令服务器撮合两者交换网络信息帮助它们建立直接的 WebRTC 连接。WebRTC 连接建立后视频流从 UE 应用直接流向网页用户输入从网页直接发回 UE 应用。4. 完整实战从 UE 到网页让我们一步步构建一个可运行的示例。4.1 步骤一创建并配置 UE 项目1. 创建新项目打开 Unreal Editor选择Games-Blank项目类型选择C或Blueprint本文选 Blueprint命名为PixelStreamingDemo创建。2. 启用像素流送插件点击菜单栏Edit-Plugins。在插件搜索框中输入Pixel Streaming。找到Pixel Streaming插件勾选Enabled。同样找到并启用Pixel Streaming下的子插件如Pixel Streaming Editor用于编辑器内预览、Pixel Streaming HMD如需VR等。重启编辑器以使插件生效。3. 配置项目设置点击Edit-Project Settings。在左侧找到Platforms-Pixel Streaming。进行关键配置Streamer保持默认Pixel Streaming。Signalling Server URL这是信令服务器的地址。我们稍后会在本地运行先设置为ws://127.0.0.1:80。生产环境需改为服务器公网 IP 或域名。Use Frontend如果使用 Epic 官方前端库可以勾选。我们自定义前端可以不勾。在Input设置中确保Default Mouse Capture Mode设置为Capture During Mouse Down或Capture Permanently以确保鼠标输入能正确发送。4. 打包项目点击Platforms-Windows-Package Project。选择输出目录例如D:\Builds\PixelStreamingDemo。等待打包完成。你会在输出目录下得到Windows文件夹里面包含.exe和相关的.dll、Content等文件。4.2 步骤二搭建信令服务器Epic 在 GitHub 上提供了官方示例。我们使用其Node.js版本。1. 获取服务器代码你可以从 Epic 的官方文档链接找到或者直接使用以下简化版本。创建一个SignalingServer文件夹并新建package.json和cirrus.js。package.json:{ name: pixel-streaming-signaling-server, version: 1.0.0, description: A simple signaling server for UE Pixel Streaming, main: cirrus.js, scripts: { start: node cirrus.js }, dependencies: { ws: ^8.14.0, express: ^4.18.2 } }cirrus.js (简化核心逻辑):const express require(express); const WebSocket require(ws); const http require(http); const app express(); const server http.createServer(app); // 静态文件服务用于托管前端页面 app.use(express.static(../WebFrontend)); // 假设前端文件在上一级的WebFrontend文件夹 const wss new WebSocket.Server({ server }); let playerSockets []; // 存储网页客户端连接 let streamerSocket null; // 存储UE应用连接 wss.on(connection, (ws, req) { const url req.url; console.log(新的连接: ${url}); // 识别连接类型 if (url.includes(streamer)) { // UE 应用连接 console.log(UE Streamer 已连接); streamerSocket ws; streamerSocket.on(close, () { console.log(UE Streamer 断开连接); streamerSocket null; }); // 转发流媒体端SDP给所有玩家 ws.on(message, (message) { playerSockets.forEach(player { if (player.readyState WebSocket.OPEN) { player.send(message.toString()); } }); }); } else { // 网页玩家连接 console.log(网页玩家已连接); playerSockets.push(ws); ws.on(close, () { console.log(网页玩家断开连接); playerSockets playerSockets.filter(player player ! ws); }); // 转发玩家端SDP给流媒体端 ws.on(message, (message) { if (streamerSocket streamerSocket.readyState WebSocket.OPEN) { streamerSocket.send(message.toString()); } }); // 如果流媒体端已存在通知新玩家开始初始化流程发送一个配置消息 if (streamerSocket) { ws.send(JSON.stringify({ type: config, peerConnectionOptions: { iceServers: [{ urls: stun:stun.l.google.com:19302 }] } })); } } }); const PORT process.env.PORT || 80; server.listen(PORT, () { console.log(信令服务器运行在 http://localhost:${PORT}); console.log(WebSocket 服务运行在 ws://localhost:${PORT}); });2. 安装并运行在SignalingServer目录下打开命令行npm install node cirrus.js如果看到信令服务器运行在 http://localhost:80的日志说明服务器启动成功。4.3 步骤三开发前端网页前端需要完成三件事1. 显示视频2. 建立 WebRTC 连接3. 捕获并发送输入事件。1. 基础 HTML 结构 (index.html):!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleUE像素流送演示/title link relstylesheet hrefstyle.css !-- 引入 Epic 官方前端库简化 WebRTC 连接和输入处理 -- script srchttps://cdn.jsdelivr.net/npm/epicgames-oss/lib-pixelstreamingfrontend-ue5.3latest/dist/index.js/script /head body div classcontainer h1Unreal Engine 像素流送演示/h1 div idvideoContainer !-- 视频将在此处渲染 -- video idvideoPlayer autoplay playsinline/video div idoverlayText正在连接 UE 服务器.../div /div div classcontrols button idconnectBtn连接流/button button idfullscreenBtn全屏/button button idemitEventBtn向UE发送事件/button input typetext idcommandInput placeholder输入命令... button idsendCommandBtn发送命令/button /div div idstatus状态: 等待连接/div div idmessageLog/div /div script srcapp.js/script /body /html2. 样式文件 (style.css):body { font-family: sans-serif; margin: 20px; background-color: #f0f0f0; } .container { max-width: 1200px; margin: 0 auto; text-align: center; } #videoContainer { position: relative; width: 1024px; height: 576px; margin: 20px auto; background-color: #000; border: 2px solid #333; } #videoPlayer { width: 100%; height: 100%; object-fit: contain; /* 保持比例适应容器 */ } #overlayText { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-size: 24px; } .controls { margin: 20px 0; } button, input { margin: 5px; padding: 10px 15px; font-size: 16px; } #status { margin-top: 15px; padding: 10px; background-color: #e8e8e8; border-radius: 5px; } #messageLog { margin-top: 20px; text-align: left; background-color: #222; color: #0f0; padding: 10px; height: 150px; overflow-y: auto; font-family: monospace; white-space: pre-wrap; }3. 核心 JavaScript 逻辑 (app.js):document.addEventListener(DOMContentLoaded, () { const videoElement document.getElementById(videoPlayer); const connectBtn document.getElementById(connectBtn); const fullscreenBtn document.getElementById(fullscreenBtn); const emitEventBtn document.getElementById(emitEventBtn); const commandInput document.getElementById(commandInput); const sendCommandBtn document.getElementById(sendCommandBtn); const statusDiv document.getElementById(status); const messageLog document.getElementById(messageLog); const overlayText document.getElementById(overlayText); let pixelStreaming null; const signalingServerUrl ws://localhost:80; // 与信令服务器配置一致 function logMessage(msg) { const timestamp new Date().toLocaleTimeString(); messageLog.innerHTML [${timestamp}] ${msg}\n; messageLog.scrollTop messageLog.scrollHeight; // 自动滚动到底部 } // 初始化 Pixel Streaming 连接 function initPixelStreaming() { if (pixelStreaming) { pixelStreaming.disconnect(); } // 使用官方库创建实例 pixelStreaming new PixelStreamingApplication({ videoElement: videoElement, initialSettings: { AutoPlayVideo: true, AutoConnect: false, // 我们手动控制连接 StartVideoMuted: false, SignallingServerUrl: signalingServerUrl } }); // 监听连接状态 pixelStreaming.addEventListener(connectionStateChanged, (state) { statusDiv.textContent 状态: ${state}; logMessage(连接状态变更为: ${state}); if (state Connected) { overlayText.style.display none; } else if (state Connecting) { overlayText.textContent 正在连接...; } else if (state Disconnected) { overlayText.textContent 连接断开; overlayText.style.display block; } }); // 监听来自 UE 的自定义消息双向通信的关键 pixelStreaming.addEventListener(message, (message) { logMessage(收到UE消息: ${message.data}); // 这里可以解析 message.data (通常是JSON)并更新网页UI或触发动作 try { const parsedMsg JSON.parse(message.data); if (parsedMsg.type playerScore) { console.log(玩家分数更新: ${parsedMsg.score}); } } catch (e) { // 非JSON消息 console.log(原始消息:, message.data); } }); // 连接到信令服务器 pixelStreaming.connect(); logMessage(正在初始化连接...); } // 按钮事件绑定 connectBtn.addEventListener(click, () { initPixelStreaming(); }); fullscreenBtn.addEventListener(click, () { if (videoElement.requestFullscreen) { videoElement.requestFullscreen(); } else if (videoElement.webkitRequestFullscreen) { videoElement.webkitRequestFullscreen(); } }); // 示例向 UE 发送一个自定义事件 emitEventBtn.addEventListener(click, () { if (pixelStreaming pixelStreaming.isConnected()) { const eventData { type: webEvent, action: buttonClicked, value: Hello from Web!, timestamp: Date.now() }; pixelStreaming.emitUIInteraction(JSON.stringify(eventData)); logMessage(发送事件到UE: ${JSON.stringify(eventData)}); } else { logMessage(错误: 未连接到UE无法发送事件。); } }); // 发送自定义命令 sendCommandBtn.addEventListener(click, () { const command commandInput.value.trim(); if (!command) return; if (pixelStreaming pixelStreaming.isConnected()) { // 使用 emitUIInteraction 或 emitCommand 发送字符串命令 pixelStreaming.emitUIInteraction(command); logMessage(发送命令: ${command}); commandInput.value ; } else { logMessage(错误: 未连接到UE无法发送命令。); } }); // 初始日志 logMessage(前端页面加载完成。点击“连接流”开始。); });4.4 步骤四运行与验证请严格按照顺序启动服务启动信令服务器 在SignalingServer目录下运行node cirrus.js。确保控制台无报错监听在 80 端口。启动 UE 应用程序 进入打包好的Windows文件夹找到.exe文件。不要直接双击运行。 我们需要通过命令行附加像素流送参数来启动。打开命令行导航到该目录cd D:\Builds\PixelStreamingDemo\Windows PixelStreamingDemo.exe -PixelStreamingURLws://127.0.0.1:80 -RenderOffScreen-PixelStreamingURL指定信令服务器地址必须与项目设置和前端代码中的一致。-RenderOffScreen让 UE 无头运行不显示本地窗口对于服务器部署是必须的。本地测试可以先不加此参数以便观察 UE 是否正常运行。如果启动成功UE 应用会尝试连接信令服务器。你会在信令服务器的控制台看到UE Streamer 已连接的日志。访问前端网页 由于我们的信令服务器 (cirrus.js) 也通过 Express 托管了静态文件你可以直接在浏览器中访问http://localhost。 点击网页上的“连接流”按钮。观察结果信令服务器日志会显示网页玩家已连接。稍等片刻网页中的overlayText会消失视频区域开始播放 UE 渲染的实时画面。你可以在网页中点击鼠标、按下键盘操作会实时反映在 UE 程序中如果 UE 窗口可见你会看到角色移动或视角变化。点击“向UE发送事件”按钮在信令服务器和 UE 的输出日志中如果配置了日志接收应该能看到相应的消息。4.5 步骤五实现双向通信进阶上面的app.js已经演示了通过emitUIInteraction发送消息。要在 UE 端接收并处理这些消息需要在 UE 项目中编写蓝图或 C 代码。在 UE 端接收网页消息 (Blueprint 示例):在 UE 编辑器中打开关卡蓝图或创建一个新的 Actor 蓝图。添加一个On Pixel Streaming事件节点。这个事件在收到前端消息时触发。从该节点的Descriptor引脚解析消息。通常消息是字符串格式的 JSON。使用Parse JSON节点将其转换为结构体或直接处理。简单蓝图逻辑示意事件Event On Pixel Streaming(StringDescriptor)分支判断Descriptor是否包含特定关键字如“webEvent”。解析 JSON 获取action和value。根据内容执行操作如生成一个 Actor、改变灯光颜色、更新 UI 等。从 UE 发送消息到网页在 UE 蓝图或 C 中可以使用Pixel Streaming插件提供的函数例如Pixel Streaming - Send Response节点蓝图。FPixelStreamingModule::Get().SendResponse()(C)。 将需要发送的数据如玩家位置、游戏状态、事件通知构造成字符串推荐 JSON 格式发送出去。网页端已经在app.js中通过pixelStreaming.addEventListener(message, ...)监听了这些消息并可以在回调函数中更新网页 DOM。5. 常见问题与排查思路在部署像素流送时你可能会遇到以下问题问题现象可能原因排查思路与解决方案网页显示黑屏/“正在连接”1. 信令服务器未启动或地址错误。2. UE 应用未启动或连接参数错误。3. 防火墙/网络阻止了 WebSocket (ws://) 连接。4. 视频编解码器不支持。1. 检查信令服务器控制台是否有UE Streamer 已连接和网页玩家已连接日志。2. 检查 UE 启动命令行参数特别是-PixelStreamingURL。3. 在 Windows 防火墙中为 Node.js 和 UE 应用添加入站规则或暂时关闭防火墙测试。4. 尝试在 Chrome/Edge 最新版中访问。有画面但操作无响应1. UE 项目输入设置问题。2. 前端输入捕获未正确绑定到视频元素。3. 网络延迟过高。1. 确认 UE 项目设置中Default Mouse Capture Mode已正确设置。2. 确保使用官方frontend.js库它自动处理了输入绑定。检查视频元素 ID 是否正确。3. 检查网络状况本地环回(127.0.0.1)延迟应极低。连接频繁断开1. 信令服务器不稳定。2. WebRTC ICE 协商失败NAT/防火墙。3. UE 应用或服务器资源CPU/GPU/内存不足。1. 检查信令服务器代码是否有未处理的异常。2. 在信令服务器和前端配置中添加 STUN 服务器如示例中的stun:stun.l.google.com:19302。对于复杂网络可能需要 TURN 服务器。3. 监控服务器资源使用情况升级配置或优化 UE 场景。画面卡顿、延迟高1. 服务器编码性能瓶颈。2. 客户端网络带宽不足或不稳定。3. 编码参数码率、分辨率设置过高。1. 确保服务器使用 GPU 硬件编码NVIDIA NVENC 或 AMD AMF。2. 降低 UE 输出分辨率如 1280x720和码率在Project Settings - Pixel Streaming中调整。3. 让客户端靠近服务器或使用 CDN 加速。无法从外网访问1. 服务器公网 IP/端口未暴露。2. 信令服务器配置中仍使用localhost或127.0.0.1。3. 云服务器安全组/防火墙未放行端口默认80、443、8888等。1. 将信令服务器地址改为0.0.0.0监听并在 UE 和前端配置中使用服务器的公网 IP 或域名。2. 配置路由器端口转发或使用云服务商的负载均衡器。3. 在云服务器控制台安全组中放行 TCP 端口如 80, 443和 UDP 端口用于 WebRTC 媒体传输范围较大通常需要放行 49152-65535。此操作需谨慎做好安全防护。6. 最佳实践与工程建议将像素流送用于生产环境需要考虑更多工程化因素安全第一信令服务器认证上述示例服务器没有认证任何知道地址的人都可以连接。生产环境必须添加认证机制如 Token、JWT在连接 WebSocket 前验证身份。HTTPS/WSS务必使用wss://和https://来加密通信防止数据被窃听或篡改。输入验证在 UE 端严格验证从网页接收到的所有命令和数据防止恶意输入导致程序崩溃或安全漏洞。性能与伸缩性独立信令服务器将信令服务器部署为独立的、可水平扩展的服务。可以使用Socket.IO或专业的 WebSocket 集群方案如Socket.IOwith Redis Adapter。UE 实例管理每个并发的 UE 实例都会消耗大量 GPU 资源。你需要一个管理系统来动态启动、分配和回收 UE 实例通常基于 Docker/Kubernetes这被称为“应用程序集群”。使用 TURN 服务器在复杂的公司网络或移动网络下P2P 的 WebRTC 可能失败。部署一个 TURN 服务器作为中继可以极大提高连接成功率。coturn是一个开源选择。前端体验优化自动重连在网络波动时前端应自动尝试重新连接信令服务器和 UE 流。加载状态与错误提示提供清晰的连接中、重试、错误等状态提示改善用户体验。自适应画质可以根据网络状况动态请求 UE 端调整视频码率或分辨率。输入处理对于触摸屏设备需要额外处理触摸事件。官方前端库通常已支持。UE 项目优化禁用不必要的特效关闭或降低运动模糊、景深等对延迟敏感的后处理效果。优化关卡加载流送开始前尽量预加载所有资源避免运行时卡顿。日志与监控在 UE 中集成日志系统将关键事件和错误发送到远程日志服务器便于排查问题。部署架构 一个典型的生产环境架构如下用户浏览器 --(WSS)-- [负载均衡器] --(内部网络)-- [信令服务器集群] | v 用户浏览器 --(WebRTC)-- [UE 应用实例 A (运行在 GPU 服务器 1)] 用户浏览器 --(WebRTC)-- [UE 应用实例 B (运行在 GPU 服务器 2)]使用 Kubernetes 可以方便地管理 UE 应用实例的 Pod根据用户排队情况自动扩缩容。通过本文的详解你应该已经掌握了 UE 像素流送从原理到实践从本地演示到生产构思的全链路知识。这套技术为在网页中嵌入高保真、可交互的 3D 体验打开了大门其应用场景远不止云游戏还包括数字孪生、产品配置器、虚拟展厅、在线教育等众多领域。