虚幻4渲染系统结构解析

本文根据小米互娱 VR 技术专家 房燕良在 MDCC 2016 移动开发者大会上的演讲整理而成,PPT 下载地址:http://download.csdn.net/detail/sinat_14921509/9639244。

 

 

 

小米互娱 VR 技术专家 房燕良

 

房燕良,从 2001 年开始,自主研发 3 代游戏引擎,发布游戏超过 10 款。代表作品有《仙剑3》、《功夫世界》、《龙online》、《神兵传奇》等。从 2007 年开始接触虚幻引擎,对虚幻引擎有深入的研究和实践。目前就职于小米,从事 VR 方面的研发工作。

【以下为演讲实录】

大家上午好!今天,我和大家分享的主题是虚幻 4 渲染系统结构解析。 内容主要包含以下几个模块:

  • 从 3D 引擎架构的角度讲解渲染系统在架构层面所处的位置以及与其他模块之间的关系;
  • 重点讲述虚幻 4 渲染系统的架构,主要从三个方面讲解: 
    • 渲染线程跟主线程的基础架构;
    • 场景管理;
    • 渲染流程控制角度详解该架构是如何设计和实现。
  • 最后分析虚幻 4 的 VR 在引擎层实现的流程。并以谷歌 VR HMD 插件为例进行讲解。

3D 引擎渲染系统


下图相当于一个 3D 引擎与渲染系统相关的几个模块。一个是资源系统,一个是材质系统,还有场景的管理,渲染相关的就是渲染管线的管理。这几个模块在下层都会调用图形 API 实现渲染功能。整个 3D 引擎包括渲染系统最核心面临的问题主要是两个:管理复杂度和效率。

 

渲染系统架构

 

复杂度是现在整个 3D 引擎包括渲染难度系数最高的,要实现各种各样的渲染效果、渲染算法以及各种各样优化算法。

“对游戏来说,效率就是生命”。——卡马克

效率,一是从图形算法方面,可变性的判定、流程控制的优化、平衡 CPU 跟 GPU 的工作。二是软件开发者一定是关心硬件的,意味着另一个核心问题是如何高效发挥 GPU 的高并发流水线的架构以及 GPU 上各种 Cache 如何能够帮助 Driver 提高命中率。

虚幻4渲染系统架构


渲染系统模块:

  • Engine/Source/Runtime,主要存放模块的源代码;
  • 核心代码模块 
    • RenderCore
    • Renderer
  • RHI 抽象层 
    • RHI(Render Hardware Interface)
    • 虚幻 4 的版本 RHI 的设计最初是基于 D3D 11 设计的,
  • RHI 实现层,现在对于主流的平台和主流的推荐 API 都有相应的实现包括: 
    • EmptyRHI
    • Windows 上 D3D11RHI
    • 苹果上 Metal
    • OpenGLdRV、VulkanRHI

 

模块

 

接下来从数据和逻辑两个方面解析虚幻 4 渲染系统,在论及引擎的数据管理与渲染的流程控制之前,我们先理解何为渲染线程。渲染线程机制是从虚幻 3 开始引入的,当时有一个开发代号叫做 Gemini,为什么要引入渲染线程,当然主要是从效率方法考虑。一个游戏最终开发出来之后实际上有三个大的模块是占每一帧时间最多,分别是渲染、游戏逻辑包括脚本更新、以及物理模拟。因此,如果把渲染和游戏逻辑更新并行起来,就可以得到一个显著的效率提升,如下图所示。如果没有渲染线程,游戏逻辑的更新和渲染是串行的,一帧所占的时间是两块执行的总和。如果使用了渲染线程之后,一帧的时间就是两者耗时最长的那个时间,这是一个理想情况,理想情况会有一个显著的渲染提升。

 

渲染线程

 

既然多了一个重要的线程,就会涉及到两个线程之间同步的问题。线程之间同步分两方面:

1、因为游戏有运行的速率控制问题,意味着对于游戏来说,往往游戏线程负载是低一些,渲染线程是控制一些,游戏线程疯狂往前跑也没有太大的意义,所以它有一个 Render Command Fence,防止游戏线程跑得太快。好比前台我们正在看的画面,如果是第N帧,渲染线程可以渲染第 N+1 帧,游戏线程可以渲染第 N+2 帧。

2、游戏线程同步场景管理增加了渲染线程后,整个游戏的复杂度大大提升了。游戏线程要修改它的数据,渲染线程也要修改它的数据,也很麻烦,容易出错。所以在虚幻情景下,使用了一个 Proxy 对象的模式去处理它,在游戏逻辑里面处理的一个游戏对象会在渲染线程里面对应一个 Proxy 对象,该Proxy 对象的游戏更新完全在渲染线程里面做。另外在渲染线程里,因为每一帧会有特定的状态数据,这些状态数据每一帧都在变,这个其实也没有太好的办法,在每一帧的时候,要把独特的数据进行拷贝。

下图是渲染线程跟主线程的基本关系,主线程会通过渲染命令的队列往渲染线程发消息,渲染线程会从命令队列里读取命令,它们之间有一个 Render Command Fence 这样一个机制。

 

场景数据管理

 

接下来看一下虚幻引擎场景的数据管理的一些核心类。虚幻引擎场景的数据管理分了两层,一层是比较熟悉的 UWorld,主要面向游戏逻辑开发,为了在上层做逻辑控制时较为方便去管理,比较方便实现上层控制逻辑。

 

核心类

 

对于渲染来说,UWorld 对应 FScene 对象,这个数据接口的设计主要面向浏览器,由FSceneRenderer 这一类,实现了两个派生类,一个是 FForwardShadingSceneRenderer 前置渲染,还有一个 FDeferredShadingSceneRenderer 就是延迟渲染。在 model 4.0 以下的,是逻辑渲染。如果是在 Shader Model 4.0 以上会选择延迟渲染。

另外有一核心的类是 FSceneViewFamily,在这一帧可以渲染的多个 view,个人理解最早是在单机游戏多人同时玩的分屏游戏,主要是游戏机上的游戏,比如极品飞车,可以选择两个人同时玩,两个人是在同一台游戏机上玩,在屏幕上就会分两个视图,比如我的游戏视图是再上一版,你的游戏视图是在下面一版。这是分类的一个出发点。现在 VR 兴起之后,要做 VR 渲染,正好也要分屏,左眼的图象在图片左边,右眼的图象在图片右边。

另外还有一类是 FViewInfo,有一个新的 view,FViewInfo 是定义在 Render 的模块里面,在新的 view 里面又渲染了一些新的模块的特定数据,每一帧会有一些自己的状态,要进行一些拷贝,这里面有一部分数据保存在这个新 view 这一类里面。

 

静态结构

 

刚才讲了场景整体,还有单个对象的数据管理,接下来就看一下渲染的流程。这里是一个伪代码,把引擎里渲染相关的一些关键步骤提取出来,这个不是全面的,只是为了突出重点,只是一些重点步骤。

  • Game线程
void UGameEngine::Tick( float DeltaSeconds, bool bIdleMode )
{
UGameEngine::RedrawViewports()
{
void FViewport::Draw( bool bShouldPresent)
{
void UGameViewportClient::Draw()
{//-- 计算ViewFamily、View的各种属性ULocalPlayer::CalcSceneView();
//-- 发送渲染FRendererModu命le令:::BFeDgrianwRSecnedneerCionmgmVainedwFamily()
//-- Draw HUD
PlayerController->MyHUD->PostRender();
}
}}}FrameEndSynvoid FRendererModule::BeginRenderingViewFamily()
{
// render proxies update
World->SendAllEndOfFrameUpdates();// Construct the scene renderer.
// This copies the view family attributes
// into its own structures.
FSceneRenderer* SceneRenderer =
FSceneRenderer::CreateSceneRenderer(ViewFamily);ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FDrawSceneCommand,
FSceneRenderer*,SceneRenderer,SceneRenderer,
{
RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
FlushPendingDeleteRHIResources_RenderThread();
});
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

接下来用伪代码的方式来看一下渲染主干的流程,首先入口还是 RenderViewFamily_RenderThread() 这个函数,第一步进行 InitViews(),首先调用 Primitive Visibility Determination 进行剪裁,然后是透明物的排序,然后是灯光的可见性,然后就是不透明物体的排序。

接下来通过很多的 pass 来实现整个渲染。首先会有一个 base pass,建立一个 base 缓冲,然后通过 base pass,填充 GBuffer 的缓冲,然后是渲染所有的灯光,后面就是渲染天光,渲染大气效果,渲染透明对象,渲染屏幕区特效,所有这些渲染完之后, SceneColor() 就完成了,最后进行后处理,最后是调用 RenderFinish()。

 

延迟渲染

 

RenderLights 粗略的逻辑是,场景所有的灯光都要调用 RenderLights() 函数,在该函数里面调用两个 Shader 去画灯光在屏幕空间的影响区域。

 

场景

 

void FDeferredShadingSceneRenderer::Render()
{
bool FDeferredShadingSceneRenderer::InitViews()
{
//-- Visibility determination.
void FSceneRenderer::ComputeViewVisibility()
{
FrustumCull();
OcclusionCull();
}//-- 透明对象排序:back to front
FTranslucentPrimSet::SortPrimitives();//determine visibility of each light
DoFrustumCullForLights();//-- Base Pass对象排序:front to backvoid FDeferredShadingSceneRenderer::SortBasePassStaticData();
}
}void FDeferredShadingSceneRenderer::{ Render()
{
//-- EarlyZPass
FDeferredShadingSceneRenderer::RenderPrePass();
RenderOcclusion();//-- Build Gbuffers
SetAndClearViewGBuffer(); FDeferredShadingSceneRender::RenderBasePass();
FSceneRenderTargets::FinishRenderingGBuffer();//-- Lighting stage
RenderDynamicSkyLighting();
RenderAtmosphere();
RenderFog();
RenderTranslucency();
RenderDistortion();
//-- post processing
SceneContext.ResolveSceneColor();
FPostProcessing::Process();
FDeferredShadingSceneRenderer::RenderFinish();
}void FDeferredShadingSceneRenderer::RenderLights()
{
foreach(FLightSceneInfoCompact light IN Scene->Lights)
{
void FDeferredShadingSceneRenderer::RenderLight(Light)
{
RHICmdList.SetBlendState(Additive Blending);
// DeferredLightVertexShaders.usf VertexShader = TDeferredLightVS; // DeferredLightPixelShaders.usf PixelShader = TDeferredLightPS;
switch(Light Type)
{
case LightType_Directional:
DrawFullScreenRectangle();
case LightType_Point:
StencilingGeometry::DrawSphere();
case LightType_Spot:
StencilingGeometry::DrawCone();
}
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

虚幻 4 的 VR 渲染


接下来主要分析虚幻 4 的 VR 渲染是如何实现的。虚幻 4 的渲染或者整个引擎实现的思路跟 Unity 差距还是很大,Unity 个人理解最大的好处就是统一性做得非常好,包括 Camera 这块也是做得非常好,Camera 不光代表一个视点,而且也管理一个渲染管线。因为里面如果实现 VR 渲染,相对来说好理解一些,很直接相当于可以放两个摄像机,一个是放左眼图象,一个放右眼图象。这样的结构非常清晰,但是不太好做一些深层次的优化。在虚幻 4 引擎里面,实际上把整个 VR 整合到整个引擎各个逻辑流程,各个模块里面,所以它能够比较好实现优化。新的 VR 主要是 Scene View Family 和Scene View 为基础的。

首先看一下代码目录,在Plugins/Runtime/GoogleVR/GoogleVRHMD等等里面。

插件有两个主要类,一个就是 GoogleVRHMD,另外是 GoogleVR HMDCustomPersent,前面讲了 VR 是把流程整合到每一步的逻辑里面去,所以它会选出来一些接口。这里只列了一些重点函数,接口都挺大的,里面的函数都非常多。

 

接口

 

谷歌 VR HMD 主要实现了两个 interface,一个是 AdjustViewRect(),这一类比较简单,上述讲每一帧开始渲染的时候,会计算新 view 的一些状态和参数,相当于有一些函数在不同的时机可以参与计算或者新的 SceneViewFamily 还有 SceneView。这个比较简单,就是模块的起始、停止。

另外还有一个就是 CalculateStereoViewOffset() 接口,这个是实现立体渲染的一些核心操作,都要实现这个接口的一些方法。这两类实际上起到一个包装 VR SDK 和黏合层的作用。

接下来从代码流程来看一下 VR 渲染相关的一些步骤。首先在引擎 Init() 的时候,会查找所有 HMD 的模块,一旦启动了这个插件,它在引擎 Init()的时候,就会创建 HMDDevice,在启动的时候才会启动 VR 渲染。

  • 创建HMD Device
//-- 在引擎启动时,会创建所有的HMD设备void UEngine::Init()
{
bool UEngine::InitializeHMDDevice()
{
for (auto HMDModuleIt = HMDModules.CreateIterator();
HMDModuleIt; ++HMDModuleIt)
{
IHeadMountedDisplayModule* HMDModule = *HMDModuleIt;
HMDDevice = HMDModule->CreateHeadMountedDisplay();
}
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

SceneViewFamily 和SceneView是如何启动VR渲染的,首先要看是不是启动立体渲染,如果是立体渲染,view会被强制设定成两个,然后会在每一帧,有一个接口,给你机会做这么几件事,一个是调整那个视口的范围,还有一个就是因为VR渲染两个摄像机的相应眼睛的位置是有一定距离的,可以去调整view的视点的距离。

  • 绘制流程入口
//-- 在View绘制时,如果是Stereo则绘制两个View
void UGameViewportClient::Draw()
{
const bool bEnableStereo =
GEngine->IsStereoscopic3D(InViewport);
int32 NumViews = bEnableStereo ? 2 : 1;
for (int32 i = 0; i < NumViews; ++i)
{
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • IStereoRendering接口调用
void UGameViewportClient::Draw() 
{
ULocalPlayer::CalcSceneView()
{ULocalPlayer::GetProjectionData() 
{
GEngine->StereoRenderingDevice->AdjustViewRect(StereoPass);
GEngine->StereoRenderingDevice->CalculateStereoViewOffset(StereoPass);
ProjectionData.ProjectionMatrix=GEngine->StereoRenderingDevice->GetStereoProjectionMatrix(StereoPass);
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

接下来看一下谷歌 VR HMD 里面插件的代码。首先通过刚才的AdjustViewRect,把viewport作一个调整,如果是左眼pass,就会调整左边的一版,如果是右边pass而就会调整右边的一版。另外通过CalculateStereoViewOffset()的方法去调整试点的位置,首先它是调用了SDK里面取得两眼同距的方法,通过计算算出眼睛view的location的偏离量。

最后是在Render,这个也是接口函数,在RenderThread里调用的一个方法,这个方法最终会调用谷歌 VR 的 API,会把普遍图象和专业图象调到VR SDK,再有它进行操作反映到手机屏幕上。

void FGoogleVRHMD::AdjustViewRect(StereoPass, int32& X,
int32& Y, uint32& SizeX, uint32& SizeY) const
{
SizeX = SizeX / 2;
if( StereoPass == eSSP_RIGHT_EYE )
X += SizeX;
}void FGoogleVRHMD::CalculateStereoViewOffset()
{
const float EyeOffset = (GetInterpupillaryDistance() * 0.5f)
* WorldToMeters;
const float PassOffset = (StereoPassType == eSSP_LEFT_EYE) ?
-EyeOffset : EyeOffset;
ViewLocation +=
ViewRotation.Quaternion().RotateVector(FVector(0,PassOffset,0
));
}void FGoogleVRHMD::RenderTexture_RenderThread()
{
gvr_distort_to_screen(GVRAPI,
SrcTexture->GetNativeResource(),
CachedDistortedRenderTextureParams,
&CachedPose,
&CachedFuturePoseTime);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432291.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

J2EE应用与移动互联网-写在前头

通过面向移动互联网的内容管理系统开发实践&#xff0c;积攒了一些J2EE应用和前端应用的知识&#xff0c;列出目录以供后期补充成册。 1.EJB的理解 1.1 EJB组件 1.2 数据库连接池 1.3 POJO 1.4 任务 1.5 容器 2.Spring生态 2.1 Spring Ioc 2.2 Spring AOP 2.3 Spring MVC 2.4 S…

【Modern OpenGL】前言

说明&#xff1a;跟着learnopengl的内容学习&#xff0c;不是纯翻译&#xff0c;只是自己整理记录。 强烈推荐原文&#xff0c;无论是内容还是排版。 原文链接 本文地址: http://blog.csdn.net/aganlengzi/article/details/50354140 欢迎来到OpenGL 欢迎来到OpenGL学习之旅&…

对象指针

向对象程序设计上机练习九&#xff08;对象指针&#xff09; Time Limit: 1000MS Memory limit: 65536K 题目描述 建立对象数组&#xff0c;内放5个学生数据&#xff08;学号是字符串类型、成绩是整型&#xff09;&#xff0c;设立max函数&#xff0c;用指向对象的指针作函数参…

如何利用计算机实现非线性转换,2013《数字信号处理》期末复习(填空选择判断)真题解析...

一、填空、选择、判断&#xff1a;1. 一线性时不变系统&#xff0c;输入为 x (n )时&#xff0c;输出为y (n ) &#xff1b;则输入为2x (n )时&#xff0c;输出为 2y(n) &#xff1b;输入为x (n-3)时&#xff0c;输出为 y(n-3) 。2. 线性时不变系统离散时间因果系统的系统函数为…

Windows FFMPEG开发环境配置

1.去FFMPEG网站上下载Dev版本的库&#xff0c;里面有我们需要的头文件和lib文件&#xff0c;然后下载Shared版本的库&#xff0c;里面有我们需要的dll文件 http://ffmpeg.zeranoe.com/builds/ 记得区分32位和64位的库&#xff0c;这里碰到一个大坑&#xff0c;就是我下载的是6…

Ant命令行操作

Ant命令行操作 Ant构建文件可以将项目编译&#xff0c;打包&#xff0c;測试&#xff0c;它是Apache软件基金会jakarta文件夹中的一个子项目&#xff0c;具有跨平台性&#xff0c;操作简单&#xff0c;并且非常easy上手。 关于Ant执行&#xff0c;能够在项目中找到build.xml直接…

如何用用计算机名访问共享打印机,局域网怎么连接共享打印机共享

在局域网内各个版本系统共存的时候&#xff0c;win7版本的计算机连接共享打印机的时候就无法调度网络资源&#xff0c;导致无法添加和使用共享打印机&#xff0c;给办公带来不便下面是小编为大家整理的关于&#xff0c;一起来看看吧!共享打印机之前的准备&#xff1a;1、确保跟…

FFmpeg - C++中使用ffmpeg库

ffmpeg库的接口都是c函数&#xff0c;其头文件也没有extern "C"的声明&#xff0c;所以在cpp文件里调用ffmpeg函数要注意了。 一般来说&#xff0c;一个用C写成的库如果想被C/C同时可以使用&#xff0c;那在头文件应该加上 #ifdef __cplusplus extern "C" …

LeetCode-Spiral Matrix-螺旋矩阵

https://oj.leetcode.com/problems/spiral-matrix/ 题目没难度&#xff0c;就是循环把每一层的螺旋打印出来即可。需要注意每次螺旋大小缩小2&#xff0c;计算相应的位置并考虑左边界等于右边界的情况。 class Solution { public:int n,m;int cn,cm;vector <int> tot;vec…

在vlan2用计算机名访问,计算机是如何访问一个网页的?vlan间如何实现通信?

昨天我们发布了关于一文讲弄懂什么是vlan、三层交换机、网关、子网掩码&#xff0c;有很多朋友问到关于网络通信的原理&#xff0c;今天我们这一篇文章&#xff0c;算是对昨天文章进行一个补充。首先我们要访问互联网&#xff0c;必须自己电脑上面有ip地址、子网掩码、网关、dn…

可变參数

C中传递參数时&#xff0c;有时候不确定參数的数量&#xff0c;这时候能够使用可变參数。例如以下&#xff1a; 1、std::initializer_list C11标准&#xff0c;使用方法类似vector&#xff0c;參数类型需同样。使用方法例如以下&#xff1a; #include <initializer_list>…

使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码

ffmpeg开源库&#xff0c;实现将bmp格式的图片编码成x264文件&#xff0c;并将编码好的H264文件解码保存为BMP文件。 实现将视频文件yuv格式保存的图片格式的测试&#xff0c;图像格式png,jpg, gif等等测试均OK 自己根据博客的代码&#xff0c;vs2010搭建的测试环境。资源下载…

川职院单招计算机考什么专业,四川单招考什么科目

2021年高职单招升学一对一咨询小艺老师:18290437291(微信)四川单招考什么科目2019年四川单招考试科目是什么&#xff0c;四川单招考试大概在几月份&#xff1f;四川单招考试都考什么内容&#xff0c;考试会不会很难&#xff1f;不同高校四川单招时间是不同的&#xff0c;一般都…

sqlserver检索函数、存储过程、视图 中的关键字

select a.name,a.[type],b.[definition] from sys.all_objects a,sys.sql_modules b where a.is_ms_shipped0 and a.object_id b.object_id--and a.[type] V and [definition] like %检索内容%order by a.name转载于:https://www.cnblogs.com/zspbolg/p/4012695.html

OpenGL 库 简介

OpenGL 库 简介 OpenGL库函数的命名方式为&#xff1a;<库前缀><根命令><可选的参数个数><可选的参数类型>。 库前缀有gl、glu、aux、glut、wgl、glx、agl等等&#xff0c;表示该函数属于OpenGL哪个库。参数的类型&#xff1a;I代表int型&#xff0c;…

figcaption html5,HTML5 figcaption 标签

实例html>html(html.cn)The Pulpit Rock is a massive cliff 604 metres (1982 feet) above Lysefjorden, opposite the Kjerag plateau, in Forsand, Ryfylke, Norway. The top of the cliff is approximately 25 by 25 metres (82 by 82 feet) square and almost flat, an…

svn代码版本管理总结

在本篇文章中, 我将会详细说明我是如何应用SVN trunk(树干)、branches(分支)和tags(标记)。这种方法同样被称为“branch always”&#xff0c;两者非常接近。可能我所介绍的并不是最好的方法&#xff0c;但是它会给新手一些解释说明&#xff0c;告诉他们trunk、branches和tags是…

C++11中enum class的使用

枚举类型(enumeration)使我们可以将一组整型常量组织在一起。和类一样&#xff0c;每个枚举类型定义了一种新的类型。枚举属于字面值常量类型。 C包含两种枚举&#xff1a;限定作用域的和不限定作用域的。这里主要介绍限定作用域的。不限定作用域的使用可以参考&#xff1a; h…

亿T算力量子计算机,终于,中国实现“量子计算优越性”,算力比最快超算快一百万亿倍...

就在今天12月4日&#xff0c;一篇由中国科学技术大学潘建伟、陆朝阳等组成的量子计算研究团队&#xff0c;发表在国际顶级科技学术期刊《Science》上的研究成果《使用光子的量子计算优势》&#xff0c;正式宣告中国终于实现了“量子计算优越性”。采用论文中所述量子技术构建的…

查找场景中的actor

UWorld* world GetWorld();for (TActorIterator<AMyActor> It(world, AMyActor::StaticClass()); It; It){AMyActor* actor *It;if (actor ! NULL){}}