UE4中实时渲染RTR中前向渲染与延迟渲染的选择

本文探讨了3D游戏引擎中的两种主要渲染技术:前向渲染和延迟渲染。前向渲染擅长灯光和材质计算,适合半透明表面,而延迟渲染则在动态光照和稳定效果上表现优异。文章建议根据项目需求混合使用,以达到最佳效果。

目前多数游戏引擎(包括UE4)在3D实时渲染方面使用两种渲染方式,即Forward Rendering(前向渲染)Deferred Rendering(延迟渲染),其特点概括如下:

 

Forward Rendering

1.着色、几何体、材质在同一环节计算
2.擅长灯光、材质计算,信息更多,有更多自由度,但不擅长混合各种不同功能,适合比较简单的应用程序
3.擅长渲染半透明表面
4.渲染简单应用时较快
5.动态光照对于性能有很大影响,不绝对,现在也在进步
6.可以使用多重采样抗锯齿(MSAA),更好的抗锯齿效果
 

Deferred Rendering

1.着色发生在延迟环节中
2.使用GBuffer缓存进行合成
3.擅长渲染动态光照
4.擅长提供稳定、可预测的高质量效果
5.涉及禁用功能时更加灵活,涉及到表面属性不灵活,设计开启、关闭功能时更灵活
6.多重采样抗锯齿(MSAA)不能用,依赖临时抗锯齿(TAA)
 
在实际项目中,要综合考虑多方面因素,首先便要根据目标平台确定目标帧数。在此基础上,制定严格的流程标准和限制,确保各环节保持高效。
当然不同渲染方式各有长短,项目中应考虑实际情景混合使用各种方案,兼顾效率与效果。同时可将部分工作量分配到预计算环节,减少硬件在实时渲染过程中在某一部分的压力。
 

个人经验

对于项目中所应使用的技术方案,在项目开始之前就应当预想到项目中可能会涉及到的问题(如硬件情况、目标效果、灯光使用情况等),并提前做出正确的选择。不可轻易朝令夕改,增加流程负担。
同时,更不可将思维固化。虽然工作中要严格遵守流程标准,但这并不意味着思想要受到框架的约束。每种技术都有其独到之处项目中虽然,项目中未用到的并不意味着无用。
开拓思路,或许便能在略偏门的技术基础上,实现跨时代的技术变革。
<think>我们使用UE5的蓝图C++结合来实现动态材质实例渲染目标的血迹效果。具体步骤:1.创建渲染目标(RenderTarget)作为血迹的“画布”。2.创建一个用于绘制的材质(DrawMaterial),它将在命中点绘制一个血迹形状(例如圆形或自定义纹理)。3.在角色材质中,将渲染目标作为遮罩纹理,混合基础纹理血迹纹理。4.当发生命中事件(如被击中)时,计算命中点的UV坐标,然后在渲染目标上绘制血迹。5.使用动态材质实例(DynamicMaterialInstance)来实时更新材质参数。下面详细说明每一步:步骤1:创建渲染目标-在内容浏览器中右键->渲染目标(RenderTarget)->创建一张512x512或1024x1024渲染目标,命名为RT_BloodSplatter。步骤2:创建绘制材质(DrawMaterial)-新建一个材质,命名为M_BloodDraw。-材质域设置为“表面(Surface)”,混合模式为“半透明(Translucent)”。-材质蓝图:使用一个纹理采样节点(例如一个圆形噪点纹理)作为画笔形状。添加两个标量参数:BrushSize(控制大小)BrushStrength(控制强度)。使用一个位置参数(Position,类型为Vector2D,默认值(0.5,0.5))作为绘制中心。通过计算像素到Position的距离,用1减去距离乘以BrushSize,再乘以BrushStrength,得到Alpha。步骤3:设置角色材质-角色材质(例如M_Character)中,将渲染目标RT_BloodSplatter作为纹理采样。-将基础颜色(BaseColor)设置为:基础纹理血迹纹理的混合(使用Lerp节点,Alpha由RT_BloodSplatter的R通道提供)。-粗糙度(Roughness)也可以混合:血迹区域更粗糙。步骤4:实现绘制逻辑(C++或蓝图)这里提供C++实现:首先,在角色类中声明必要的变量函数:```cpp//在角色头文件(.h)中public://渲染目标UPROPERTY(EditDefaultsOnly,Category="Blood")UTextureRenderTarget2D*BloodRenderTarget;//用于绘制的材质UPROPERTY(EditDefaultsOnly,Category="Blood")UMaterialInterface*BloodDrawMaterial;//动态材质实例,用于绘制UPROPERTY()UMaterialInstanceDynamic*BloodDrawMaterialInstance;//角色的动态材质实例(用于网格体)UPROPERTY()UMaterialInstanceDynamic*CharacterMaterialInstance;//应用血迹的函数UFUNCTION(BlueprintCallable,Category="Blood")voidApplyBloodSplatter(FVectorHitLocation);protected://初始化virtualvoidBeginPlay()override;```在角色源文件(.cpp)中实现:```cpp#include"Kismet/KismetRenderingLibrary.h"#include"Kismet/KismetSystemLibrary.h"#include"Components/SkeletalMeshComponent.h"voidAMyCharacter::BeginPlay(){Super::BeginPlay();//创建绘制用的动态材质实例if(BloodDrawMaterial){BloodDrawMaterialInstance=UMaterialInstanceDynamic::Create(BloodDrawMaterial,this);}//创建角色材质的动态实例USkeletalMeshComponent*Mesh=GetMesh();if(Mesh&&Mesh->GetMaterial(0)){CharacterMaterialInstance=Mesh->CreateAndSetMaterialInstanceDynamic(0);//将渲染目标设置为材质参数if(CharacterMaterialInstance&&BloodRenderTarget){CharacterMaterialInstance->SetTextureParameterValue("BloodMask",BloodRenderTarget);}}}voidAMyCharacter::ApplyBloodSplatter(FVectorHitLocation){if(!BloodDrawMaterialInstance||!BloodRenderTarget||!GetMesh())return;//将世界坐标HitLocation转换为网格体局部空间的UV坐标FVector2DUV;boolbResult=UKismetSystemLibrary::FindCollisionUV(HitLocation,0,//碰撞索引,通常为0GetMesh(),FName("None"),//骨骼名称,如果没有指定骨骼则用"None"UV);if(bResult){//设置绘制材质的参数:位置大小BloodDrawMaterialInstance->SetScalarParameterValue("BrushSize",0.1f);//大小,可调整BloodDrawMaterialInstance->SetScalarParameterValue("BrushStrength",1.0f);BloodDrawMaterialInstance->SetVectorParameterValue("Position",FLinearColor(UV.X,UV.Y,0,0));//将绘制材质绘制到渲染目标上(叠加模式)UKismetRenderingLibrary::DrawMaterialToRenderTarget(GetWorld(),BloodRenderTarget,BloodDrawMaterialInstance);}}```步骤5:在蓝图中调用-当角色被击中时,在蓝图中调用ApplyBloodSplatter函数,传入命中位置。注意:为了叠加多次血迹,渲染目标的初始内容应该是透明的。因此,在开始游戏,需要清空渲染目标(可以在编辑器中设置初始纹理为全透明,或者在BeginPlay时清空)。清空渲染目标的方法(在BeginPlay中):```cpp//清空渲染目标为透明UKismetRenderingLibrary::ClearRenderTarget2D(GetWorld(),BloodRenderTarget,FLinearColor::Transparent);```步骤6:保存血迹状态-如果需要保存血迹状态(例如存档),可以将渲染目标的内容保存为纹理2D(使用Texture2D),然后序列化。这样,我们就实现了使用动态材质实例渲染目标在角色衣服上绘制并保留血迹的效果。下面提供一个简单的绘制材质(M_BloodDraw)的节点图描述(因为无法直接写代码,用文字描述):-位置参数:Position(Vector2D)-大小参数:BrushSize(Scalar)-强度参数:BrushStrength(Scalar)-使用TextureSample节点采样一个圆形纹理(或噪点纹理)-计算UV到Position的距离:Distance节点(输入UVPosition)-用1减去距离:1-Distance-乘以BrushSize:乘以一个标量(控制血迹大小)-用Saturate节点钳制在0-1-乘以BrushStrength(控制血迹强度)-输出到自发光(Emissive)透明度(Opacity)注意:绘制材质是用于绘制到渲染目标上的,所以它本身是一个“画笔”,输出颜色透明度。最后,在角色材质中,使用渲染目标作为遮罩,混合基础纹理血迹纹理(血迹纹理可以是一个血渍的图片)。示例角色材质节点:-BaseColor:Lerp(TextureSample_Base,TextureSample_Blood,TextureSample_RT_BloodSplatter.R)-Roughness:Lerp(BaseRoughness,BloodRoughness,TextureSample_RT_BloodSplatter.R)这样,当渲染目标上被画上红色(或其他颜色)时,角色材质就会显示出血液。注意:渲染目标默认是RGBA格式,所以我们可以使用R通道作为遮罩。以上是具体实现方法。下面提出相关问题:</think>### UE5 使用动态材质实例渲染目标实现血迹效果的详细方法 下面我将详细解释如何在UE5中通过动态材质实例渲染目标实现高质量的血迹效果,并提供完整的代码实现。 --- #### 核心实现步骤 1. **创建渲染目标**:作为血迹的"画布" 2. **设置绘制材质**:定义血迹形状特性 3. **创建角色材质**:混合基础材质血迹效果 4. **实现绘制逻辑**:处理命中事件并绘制血迹 5. **持久化保存**:保存血迹状态到存档 --- ### 完整代码实现 #### 1. 渲染目标设置 (C++) ```cpp // 在角色头文件中声明 UPROPERTY(EditDefaultsOnly, Category = "Blood System") UTextureRenderTarget2D* BloodRenderTarget; UPROPERTY(EditDefaultsOnly, Category = "Blood System") UMaterialInterface* BloodDrawMaterial; UPROPERTY(BlueprintReadOnly, Category = "Blood System") UMaterialInstanceDynamic* BloodDrawMaterialInstance; UPROPERTY(BlueprintReadOnly, Category = "Blood System") UMaterialInstanceDynamic* CharacterMaterialInstance; UFUNCTION(BlueprintCallable, Category = "Blood System") void InitializeBloodSystem(); // 在源文件中实现 void AMyCharacter::InitializeBloodSystem() { // 创建渲染目标 (如果未设置) if (!BloodRenderTarget) { BloodRenderTarget = NewObject<UTextureRenderTarget2D>(this); BloodRenderTarget->InitAutoFormat(1024, 1024); // 1024x1024 RGBA8 BloodRenderTarget->UpdateResourceImmediate(true); } // 创建绘制材质实例 if (BloodDrawMaterial) { BloodDrawMaterialInstance = UMaterialInstanceDynamic::Create(BloodDrawMaterial, this); } // 创建角色材质实例 if (USkeletalMeshComponent* Mesh = GetMesh()) { if (UMaterialInterface* BaseMaterial = Mesh->GetMaterial(0)) { CharacterMaterialInstance = UMaterialInstanceDynamic::Create(BaseMaterial, this); Mesh->SetMaterial(0, CharacterMaterialInstance); // 连接渲染目标到角色材质 CharacterMaterialInstance->SetTextureParameterValue("BloodMask", BloodRenderTarget); } } // 清空渲染目标 (初始透明) UKismetRenderingLibrary::ClearRenderTarget2D(this, BloodRenderTarget, FLinearColor::Transparent); } ``` #### 2. 绘制材质设置 (材质蓝图) 创建材质`M_BloodDraw`,设置如下节点: ``` [位置] (Vector2参数) -> [UV坐标] [血迹形状] (纹理采样) -> [颜色] [血迹大小] (标量参数) -> [缩放] [血迹强度] (标量参数) -> [乘法] ↓ [输出到渲染目标] ``` #### 3. 角色材质设置 (材质蓝图) 创建材质`M_Character`,混合基础材质血迹: ```cpp BaseColor = Lerp( OriginalBaseColor, BloodColor, BloodMaskTexture.R ) Roughness = Lerp( OriginalRoughness, BloodRoughness, BloodMaskTexture.R ) Metallic = Lerp( OriginalMetallic, BloodMetallic, BloodMaskTexture.G ) ``` #### 4. 血迹绘制逻辑 (C++) ```cpp void AMyCharacter::ApplyBloodSplatter(FVector HitLocation, FVector HitNormal, float Intensity) { if (!BloodDrawMaterialInstance || !BloodRenderTarget || !GetMesh()) return; // 计算命中点的UV坐标 FVector2D UV; bool bHitUVValid = GetHitUV(HitLocation, HitNormal, UV); if (bHitUVValid) { // 设置绘制材质参数 BloodDrawMaterialInstance->SetScalarParameterValue("BrushSize", FMath::RandRange(0.05f, 0.2f)); BloodDrawMaterialInstance->SetScalarParameterValue("BrushStrength", Intensity); BloodDrawMaterialInstance->SetVectorParameterValue("Position", FLinearColor(UV.X, UV.Y, 0, 0)); // 绘制到渲染目标 UKismetRenderingLibrary::DrawMaterialToRenderTarget( GetWorld(), BloodRenderTarget, BloodDrawMaterialInstance ); // 更新血迹强度 CurrentBloodIntensity = FMath::Clamp(CurrentBloodIntensity + Intensity, 0.0f, 1.0f); CharacterMaterialInstance->SetScalarParameterValue("BloodIntensity", CurrentBloodIntensity); } } bool AMyCharacter::GetHitUV(FVector HitLocation, FVector HitNormal, FVector2D& OutUV) { USkeletalMeshComponent* Mesh = GetMesh(); if (!Mesh) return false; // 转换为网格局部空间 FVector LocalHit = Mesh->GetComponentTransform().InverseTransformPosition(HitLocation); FVector LocalNormal = Mesh->GetComponentTransform().InverseTransformVectorNoScale(HitNormal); // 查找最近的三角形 FVector ClosestPoint; int32 TriangleIndex; FVector BaryCoords; float Distance; bool bFound = Mesh->FindClosestPointOnPhysicsAsset( LocalHit, ClosestPoint, TriangleIndex, BaryCoords, Distance ); if (bFound) { // 获取UV坐标 FVector2D UV0, UV1, UV2; Mesh->GetPhysicsTriMeshUVs(TriangleIndex, UV0, UV1, UV2); // 重心坐标插值 OutUV = UV0 * BaryCoords.X + UV1 * BaryCoords.Y + UV2 * BaryCoords.Z; return true; } return false; } ``` #### 5. 血迹持久化保存 (C++) ```cpp // 保存血迹状态 void AMyCharacter::SaveBloodState(FSaveGameData& SaveData) { SaveData.BloodIntensity = CurrentBloodIntensity; // 将渲染目标保存为纹理 UTexture2D* BloodTexture = UKismetRenderingLibrary::RenderTargetCreateStaticTexture2D( BloodRenderTarget, FString(TEXT("SavedBloodMask")) ); SaveData.BloodTexture = BloodTexture; } // 加载血迹状态 void AMyCharacter::LoadBloodState(const FSaveGameData& SaveData) { CurrentBloodIntensity = SaveData.BloodIntensity; CharacterMaterialInstance->SetScalarParameterValue("BloodIntensity", CurrentBloodIntensity); // 恢复渲染目标 if (SaveData.BloodTexture) { UKismetRenderingLibrary::ClearRenderTarget2D(this, BloodRenderTarget, FLinearColor::Transparent); UKismetRenderingLibrary::DrawTextureToRenderTarget( this, BloodRenderTarget, SaveData.BloodTexture ); } } ``` --- ### 关键优化技术 1. **UV坐标精度优化**: ```cpp // 使用高精度UV计算 FVector2D UV = Mesh->GetClosestUV( HitLocation, INDEX_NONE, FVector::ZeroVector, nullptr, nullptr, true // 高精度模式 ); ``` 2. **多层血迹叠加**: ```cpp // 在绘制材质中实现 FinalAlpha = 1 - (1 - Layer1) * (1 - Layer2) * (1 - Layer3); ``` 3. **血迹消退系统**: ```cpp // 每帧更新 void AMyCharacter::Tick(float DeltaTime) { Super::Tick(DeltaTime); // 血迹随时间淡化 if (CurrentBloodIntensity > 0) { float NewIntensity = FMath::FInterpConstantTo( CurrentBloodIntensity, 0.0f, DeltaTime, FadeRate ); if (NewIntensity != CurrentBloodIntensity) { CurrentBloodIntensity = NewIntensity; CharacterMaterialInstance->SetScalarParameterValue("BloodIntensity", NewIntensity); } } } ``` 4. **LOD优化**: ```cpp // 根据距离降低血迹分辨率 float Distance = (CameraLocation - GetActorLocation()).Size(); int32 Resolution = FMath::Clamp(FMath::TruncToInt(1024 * (1 - Distance / 5000)), 128, 1024); if (Resolution != BloodRenderTarget->SizeX) { BloodRenderTarget->ResizeTarget(Resolution, Resolution); } ``` --- ### 蓝图集成示例 1. **事件图表**: ```blueprint Event Hit: Apply Blood Splatter [Location: Hit.Location, Normal: Hit.Normal, Intensity: 0.3] ``` 2. **初始化**: ```blueprint Event BeginPlay: Initialize Blood System ``` 3. **存档系统**: ```blueprint Event SaveGame: Call Save Blood State Event LoadGame: Call Load Blood State ``` --- ### 常见问题解决方案 1. **血迹边缘锯齿**: - 在绘制材质中使用距离场抗锯齿 - 开启渲染目标的mipmap生成 ```cpp BloodRenderTarget->bAutoGenerateMips = true; ``` 2. **性能优化**: - 限制每帧最大绘制次数 - 使用异步渲染目标更新 ```cpp UKismetRenderingLibrary::DrawMaterialToRenderTarget_Async( this, BloodRenderTarget, BloodDrawMaterialInstance ); ``` 3. **动态衣物支持**: - 为不同衣物部位使用单独的渲染目标 - 通过材质参数集合统一管理 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值