UE4之StaticMesh和SkeletalMesh类关系图
StaticMesh类关系图
StaticMesh渲染数据结构
SkeletalMesh类关系图
USkinnedMeshComponent::CreateRenderState_Concurrent函数
void USkinnedMeshComponent::CreateRenderState_Concurrent(FRegisterComponentContext* Context) { LLM_SCOPE(ELLMTag::SkeletalMesh); if( SkeletalMesh ) { // Attempting to track down UE-45505, where it looks as if somehow a skeletal mesh component's mesh has only been partially loaded, causing a mismatch in the LOD arrays checkf(!SkeletalMesh->HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_WillBeLoaded), TEXT("Attempting to create render state for a skeletal mesh that is is not fully loaded. Mesh: %s"), *SkeletalMesh->GetName()); // Initialize the alternate weight tracks if present BEFORE creating the new mesh object InitLODInfos(); // No need to create the mesh object if we aren't actually rendering anything (see UPrimitiveComponent::Attach) if ( FApp::CanEverRender() && ShouldComponentAddToScene() ) { ERHIFeatureLevel::Type SceneFeatureLevel = GetWorld()->FeatureLevel; FSkeletalMeshRenderData* SkelMeshRenderData = SkeletalMesh->GetResourceForRendering(); int32 MinLODIndex = ComputeMinLOD(); #if DO_CHECK for (int LODIndex = MinLODIndex; LODIndex < SkelMeshRenderData->LODRenderData.Num(); LODIndex++) { FSkeletalMeshLODRenderData& LODData = SkelMeshRenderData->LODRenderData[LODIndex]; const FPositionVertexBuffer* PositionVertexBufferPtr = &LODData.StaticVertexBuffers.PositionVertexBuffer; if (!PositionVertexBufferPtr || (PositionVertexBufferPtr->GetNumVertices() <= 0)) { UE_LOG(LogSkinnedMeshComp, Warning, TEXT("Invalid Lod %i for Rendering Asset: %s"), LODIndex, *SkeletalMesh->GetFullName()); } } #endif // Also check if skeletal mesh has too many bones/chunk for GPU skinning. if (bRenderStatic) { // GPU skin vertex buffer + LocalVertexFactory MeshObject = ::new FSkeletalMeshObjectStatic(this, SkelMeshRenderData, SceneFeatureLevel); } else if(ShouldCPUSkin()) { MeshObject = ::new FSkeletalMeshObjectCPUSkin(this, SkelMeshRenderData, SceneFeatureLevel); } // don't silently enable CPU skinning for unsupported meshes, just do not render them, so their absence can be noticed and fixed else if (!SkelMeshRenderData->RequiresCPUSkinning(SceneFeatureLevel, MinLODIndex)) { MeshObject = ::new FSkeletalMeshObjectGPUSkin(this, SkelMeshRenderData, SceneFeatureLevel); } else { int32 MaxBonesPerChunk = SkelMeshRenderData->GetMaxBonesPerSection(MinLODIndex); int32 MaxSupportedGPUSkinBones = FMath::Min(GetFeatureLevelMaxNumberOfBones(SceneFeatureLevel), FGPUBaseSkinVertexFactory::GetMaxGPUSkinBones()); int32 NumBoneInfluences = SkelMeshRenderData->GetNumBoneInfluences(MinLODIndex); FString FeatureLevelName; GetFeatureLevelName(SceneFeatureLevel, FeatureLevelName); UE_LOG(LogSkinnedMeshComp, Warning, TEXT("SkeletalMesh %s, is not supported for current feature level (%s) and will not be rendered. MinLOD %d, NumBones %d (supported %d), NumBoneInfluences: %d"), *GetNameSafe(SkeletalMesh), *FeatureLevelName, MinLODIndex, MaxBonesPerChunk, MaxSupportedGPUSkinBones, NumBoneInfluences); } //Allow the editor a chance to manipulate it before its added to the scene PostInitMeshObject(MeshObject); } } Super::CreateRenderState_Concurrent(Context); if (SkeletalMesh) { // Update dynamic data if(MeshObject) { // Clamp LOD within the VALID range // This is just to re-verify if LOD is WITHIN the valid range // Do not replace this with UpdateLODStatus, which could change the LOD // without animated, causing random skinning issues // This can happen if your MinLOD is not valid anymore after loading // which causes meshes to be invisible { int32 MinLodIndex = ComputeMinLOD(); int32 MaxLODIndex = MeshObject->GetSkeletalMeshRenderData().LODRenderData.Num() - 1; PredictedLODLevel = FMath::Clamp(PredictedLODLevel, MinLodIndex, MaxLODIndex); } // If we have a valid LOD, set up required data, during reimport we may try to create data before we have all the LODs // imported, in that case we skip until we have all the LODs if(SkeletalMesh->IsValidLODIndex(PredictedLODLevel)) { const bool bMorphTargetsAllowed = CVarEnableMorphTargets.GetValueOnAnyThread(true) != 0; // Are morph targets disabled for this LOD? if (bDisableMorphTarget || !bMorphTargetsAllowed) { ActiveMorphTargets.Empty(); } MeshObject->Update(PredictedLODLevel, this, ActiveMorphTargets, MorphTargetWeights, EPreviousBoneTransformUpdateMode::UpdatePrevious); // send to rendering thread } } // scene proxy update of material usage based on active morphs UpdateMorphMaterialUsageOnProxy(); } }
勾选Render Static,会走FSkeletalMeshObjectStatic逻辑:
SkeletalMesh渲染数据结构(SkeletalMesh)
注1:勾选了Use Unlimited Bone Influences,FSkinWeightVertexBuffer中使用FSkinWeightLookupVertexBuffer来存储骨骼权重数据,否则使用FSkinWeightDataVertexBuffer来存储
注2:勾选Support 16-bit Bone Index,最多只能使用65536根骨骼,但可以有效降低骨骼IndexBuffer显存的占用
SkeletalMesh渲染数据结构(FSkeletalMeshObject)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2017-04-27 Unity端游无法下载资源问题