Summary
On older materials, Blend Mode of Masked doesn’t work. And there is no setting that you can turn on or off to make it work ever. That material file is now useless. I have no idea what the cause is or when this happened.
If you create a new material from scratch, it works.
What Type of Bug are you experiencing?
Rendering (Graphics / Niagara)
Steps to Reproduce
Not sure what the steps would be. Best guess is to create a material with a single texture to color in an older version of UE. Then in 5.8.1, enable substrate. Then try to use Blend Mode of Masked. Not sure if it’s related to substrate though.
Changing the blend mode to Masked will ignore the Opacity Mask pin. Even if you use a substrate slab, it will still ignore Opacity Mask.
Expected Result
Blend Mode of Masked should work. Setting OpacityMask to 0 should make material invisible.
Observed Result
Mesh (and material preview) stays visible with Opacity Mask of 0 when using Blend Mode of Masked.
Affects Versions
5.8
Platform(s)
Windows
Upload an image
Additional Notes
The HLSL output shows the following on older materials even if you have 0 going into the OpacityMask pin (see attached image):
PixelMaterialInputs.OpacityMask = 1.00000000f;
On a new material created from scratch, the correct value is used:
PixelMaterialInputs.OpacityMask = 0.00000000f;
1 Like
Here is something that was happening to me that I didn’t realize was connected. If you create a material instance of a material that has this issue, the instance will default to Opaque. It seems it doesn’t recognize the change to Blend Mode of Masked in the main material.
If you override the blend mode in the material instance, the instance will then work correctly (while the main material still remains broken).
I found the culprit, but still not sure how it got set. And still need a proper fix. There is a hidden property on the Material called bCanMaskedBeAssumedOpaque. It is set to true. How was it set? I have no idea. One piece of code I saw was that if it was an old UE4 version from FArchive and bIsMasked_DEPRECATED is false (which it usually is), then it would set and write that value as true.
So for this fix, I think you have to resave the file first to stop it from writing true. Then you can run this C++ function on it. This may be overkill, but it works. You can write an Editor Utility Widget to call this on your material if you want.
void UMyLibraryClass::FixMaterial(UMaterial* Material)
{
if (!Material) return;
Material->bCanMaskedBeAssumedOpaque = false;
Material->Modify();
EBlendMode TargetBlendMode = Material->BlendMode;
// Switch blend mode to force re-evaluation
Material->BlendMode = BLEND_Translucent;
Material->PreEditChange(nullptr);
Material->PostEditChange();
// Switch it back.
Material->BlendMode = TargetBlendMode;
Material->bCanMaskedBeAssumedOpaque = false;
Material->PreEditChange(nullptr);
Material->PostEditChange();
Material->ForceRecompileForRendering();
UE_LOG(LogTemp, Log, TEXT("Done material sanitation for: %s"), *Material->GetName());
}
Now that I have a tool to check this flag, I have found that it is on EVERYTHING! Even materials I created not long ago. Some were created in 5.7. Everything I’m using from Fab has the flag. Everything from Megascans has it. I’m surprised this hasn’t come up earlier.
Only materials I’ve created in 5.8 work correctly with Blend Mode of Masked.
Here’s a C++ function to fix all materials in one go. If you get warnings when it runs, that’s fine. It switches to translucent and back to try and force recompiling. Some materials don’t work with translucent blend mode and that’s fine. It will still fix it.
This code was created by Gemini.
Header:
#if WITH_EDITOR
UFUNCTION(BlueprintCallable)
static void FixAllMaterials();
#endif
cpp
#if WITH_EDITOR
void UMyLibraryClass::FixAllMaterials()
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
FARFilter Filter;
Filter.PackagePaths.Add(FName("/Game"));
Filter.bRecursivePaths = true;
Filter.ClassPaths.Add(UMaterial::StaticClass()->GetClassPathName());
TArray<FAssetData> AssetList;
AssetRegistry.GetAssets(Filter, AssetList);
int32 FixedCount = 0;
for (const FAssetData& AssetData : AssetList)
{
if (AssetData.AssetClassPath.GetAssetName() == FName("Material"))
{
// Instantly loads the object from its registry definition safely
UMaterial* Material = Cast<UMaterial>(AssetData.GetAsset());
// Check if the sticky legacy optimization variable is active
if (Material && Material->bCanMaskedBeAssumedOpaque)
{
// Execute our structural blend-mode flip fix we discovered!
Material->bCanMaskedBeAssumedOpaque = false;
Material->Modify();
EBlendMode TrueBlendMode = Material->BlendMode;
// Loop the layout compiler to force-wipe the legacy data
Material->BlendMode = BLEND_Translucent;
Material->PreEditChange(nullptr);
Material->PostEditChange();
Material->BlendMode = TrueBlendMode;
Material->bCanMaskedBeAssumedOpaque = false;
Material->PreEditChange(nullptr);
Material->PostEditChange();
Material->ForceRecompileForRendering();
FixedCount++;
UE_LOG(LogTemp, Log, TEXT("Fixed asset: %s"), *Material->GetName());
}
}
}
UE_LOG(LogTemp, Display, TEXT("--- BATCH FIX COMPLETE --- Processed and saved %d corrupted materials."), FixedCount);
}
#endif
Then just create an Editor Blueprint Widget. Add a button and have it call this function. Run the widget and click the button. It will fix all your materials in one go.
It only fixes the ones with that flag. If it doesn’t have the flag, it skips it.