之前我有写过两篇通过自定义场景代理来实现描边的文章:
https://zhuanlan.zhihu.com/p/69139579
https://zhuanlan.zhihu.com/p/69156465
当时有一个问题没解决,就是不知道如何将材质设置为反向剔除,因为UE4只有双面渲染选项,毕竟只有卡通渲染等一些非真实渲染才需要这种功能。但经过MeshDraw框架的学习,我发现只需要在场景代理中设置一下FMeshBatch剔除属性即可,而且这个方法没有任何副作用。其他代码可以参考我的插画。效果如图(没有使用深度偏移)
StaticMesh
StaticMesh的改法非常简单,只需要在绘制前加上一句MeshBatch.ReverseCulling = !MeshBatch.ReverseCulling;
即可。
FMeshBatch MeshBatch;
if (GetShadowMeshElement(LODIndex, BatchIndex, PrimitiveDPG, MeshBatch, bAllSectionsUseDitheredLODTransition))
{
bUseUnifiedMeshForShadow = bAllSectionsCastShadow;
MeshBatch.CastShadow = bUseUnifiedMeshForShadow;
MeshBatch.bUseForDepthPass = bUseUnifiedMeshForDepth;
MeshBatch.bUseAsOccluder = bUseUnifiedMeshForDepth;
MeshBatch.bUseForMaterial = false;
PDI->DrawMesh(MeshBatch, ScreenSize);
const FLODInfo& ProxyLODInfo = LODs[LODIndex];
UMaterialInterface* MaterialInterface = ProxyLODInfo.Sections[0].Material;
const UOutlineStaticMeshComponent* OutlineStaticMeshComponent = dynamic_cast<const UOutlineStaticMeshComponent *>(ComponentPtr);
if (MaterialInterface == OutlineStaticMeshComponent->OutlinePassMaterial)
{
continue;
}
if (OutlineStaticMeshComponent->NeedOutlinePass) {
if (OutlineStaticMeshComponent->OutlinePassMaterial == nullptr) {
continue;
}
MeshBatch.MaterialRenderProxy = OutlineStaticMeshComponent->OutlinePassMaterial->GetRenderProxy();
MeshBatch.ReverseCulling = !MeshBatch.ReverseCulling;
PDI->DrawMesh(MeshBatch, FLT_MAX);
}
}
SkeletalMesh
SkeletalMesh会比较麻烦因为一些关键函数是private、protect类型的或是类型没有声明在h中。
- 重写FSkeletalMeshSceneProxy::GetDynamicElementsSection()
- 实现CreateBaseMeshBatch(),其中BatchElement.VertexFactoryUserData需要GPUSkinCacheEntry,这就需要一些小技巧了。
- 你需要使用继承FSkeletalMeshObjectGPUSkin,再编写Public函数来取得SkinCacheEntry。
- 创建FGPUSkinCacheEntry(从源码里复制即可,再把一些不可访问的函数略加去除即可,只需要保证类内的变量内存结构与原版的相同即可)
具体可以参考:https://gist.github.com/blueroseslol/5ff2c26fc3b77305a88b2378772527ac
额外思考
其实可以看到FMeshBatch还是指定顶点工厂,那是不是在指定了自定义顶点工厂,再修改BasePassVertexShader.usf与BasePassPixelShader.usf就可以实现添加顶点数据的功能呢?
不过因为本人为非游戏及影视的UE4业余爱好者,时间与心力不足,只能转行以后再探究了。