Unreal材质面板添加全局属性节点
Unreal添加全局的材质变量一般通过材质收集器来实现,这篇博客提供另一种内嵌到材质蓝图节点上的方法,在实现的过程中,也能理解一下Unreal的材质蓝图是怎么工作的。Unreal材质蓝图相关的引擎层代码在这个路径下:\UnrealEngine\Engine\Source\Runtime\Engine\Classes\Materials。在材质蓝图的节点基本都继承自UMaterialExpression,然后实现自己的Compile方法,通过调用HLSLMaterialTranslator来翻译出HLSL代码块 。每个节点有输入和输出,通过拓扑排序的方式去处理整个节点树,直到输出到材质的Result节点上。然后就会生成一个void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)这样的方法。其中FMaterialPixelParameters里存放了一些基本常量,如:
struct FMaterialPixelParameters
{
#if NUM_TEX_COORD_INTERPOLATORS
float2 TexCoords;
#endif
/** Interpolated vertex color, in linear color space. */
half4 VertexColor;
/** Normalized world space normal. */
half3 WorldNormal;
/** Normalized world space reflected camera vector. */
half3 ReflectionVector;
/** Normalized world space camera vector, which is the vector from the point being shaded to the camera position. */
half3 CameraVector;
/** World space light vector, only valid when rendering a light function. */
half3 LightVector;
/**
* Like SV_Position (.xy is pixel position at pixel center, z:DeviceZ, .w:SceneDepth)
* using shader generated value SV_POSITION
* Note: this is not relative to the current viewport. RelativePixelPosition = MaterialParameters.SvPosition.xy - View.ViewRectMin.xy;
*/
float4 SvPosition;
/** Post projection position reconstructed from SvPosition, before the divide by W. left..top -1..1, bottom..top -1..1 within the viewport, W is the SceneDepth */
float4 ScreenPosition;
half UnMirrored;
half TwoSidedSign;
/**
* Orthonormal rotation-only transform from tangent space to world space
* The transpose(TangentToWorld) is WorldToTangent, and TangentToWorld is WorldVertexNormal
*/
half3x3 TangentToWorld;
#if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION
/** World vertex normal interpolated at the pixel center that is safe to use for derivatives. */
half3 WorldVertexNormal_Center;
#endif
/**
* Interpolated worldspace position of this pixel
* todo: Make this TranslatedWorldPosition and also rename the VS/DS/HS WorldPosition to be TranslatedWorldPosition
*/
float3 AbsoluteWorldPosition;
/**
* Interpolated worldspace position of this pixel, centered around the camera
*/
float3 WorldPosition_CamRelative;
/**
* Interpolated worldspace position of this pixel, not including any world position offset or displacement.
* Only valid if shader is compiled with NEEDS_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS, otherwise just contains 0
*/
float3 WorldPosition_NoOffsets;
/**
* Interpolated worldspace position of this pixel, not including any world position offset or displacement.
* Only valid if shader is compiled with NEEDS_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS, otherwise just contains 0
*/
float3 WorldPosition_NoOffsets_CamRelative;
/** Offset applied to the lighting position for translucency, used to break up aliasing artifacts. */
half3 LightingPositionOffset;
float AOMaterialMask;
#if LIGHTMAP_UV_ACCESS
float2 LightmapUVs;
#endif
#if USE_INSTANCING
half4 PerInstanceParams;
#endif
// Index into View.PrimitiveSceneData
uint PrimitiveId;
/** Per-particle properties. Only valid for particle vertex factories. */
FMaterialParticleParameters Particle;
#if (ES2_PROFILE || ES3_1_PROFILE)
float4 LayerWeights;
#endif
#if TEX_COORD_SCALE_ANALYSIS
/** Parameters used by the MaterialTexCoordScales shader. */
FTexCoordScalesParams TexCoordScalesParams;
#endif
#if POST_PROCESS_MATERIAL && (FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1)
/** Used in mobile custom pp material to preserve original SceneColor Alpha */
half BackupSceneColorAlpha;
#endif
#if COMPILER_HLSL
// Workaround for &#34;error X3067: &#39;GetObjectWorldPosition&#39;: ambiguous function call&#34;
// Which happens when FMaterialPixelParameters and FMaterialVertexParameters have the same number of floats with the HLSL compiler ver 9.29.952.3111
// Function overload resolution appears to identify types based on how many floats / ints / etc they contain
uint Dummy;
#endif
#if NUM_VIRTUALTEXTURE_SAMPLES || LIGHTMAP_VT_ENABLED
FVirtualTextureFeedbackParams VirtualTextureFeedback;
#endif
};FPixelMaterialInputs则是材质蓝图的Result节点相关的属性,这个方法样例贴一下。
void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)
{
// Initial calculations (required for Normal)
// The Normal is a special case as it might have its own expressions and also be used to calculate other inputs, so perform the assignment here
PixelMaterialInputs.Normal = MaterialFloat3(0.00000000,0.00000000,1.00000000);
// Note that here MaterialNormal can be in world space or tangent space
float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs);
#if MATERIAL_TANGENTSPACENORMAL
#if SIMPLE_FORWARD_SHADING
Parameters.WorldNormal = float3(0, 0, 1);
#endif
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM4
// ES2 will rely on only the final normalize for performance
MaterialNormal = normalize(MaterialNormal);
#endif
// normalizing after the tangent space to world space conversion improves quality with sheared bases (UV layout to WS causes shrearing)
// use full precision normalize to avoid overflows
Parameters.WorldNormal = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal);
#else //MATERIAL_TANGENTSPACENORMAL
Parameters.WorldNormal = normalize(MaterialNormal);
#endif //MATERIAL_TANGENTSPACENORMAL
#if MATERIAL_TANGENTSPACENORMAL
// flip the normal for backfaces being rendered with a two-sided material
Parameters.WorldNormal *= Parameters.TwoSidedSign;
#endif
Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false);
#if !PARTICLE_SPRITE_FACTORY
Parameters.Particle.MotionBlurFade = 1.0f;
#endif // !PARTICLE_SPRITE_FACTORY
// Now the rest of the inputs
MaterialFloat4 Local0 = ProcessMaterialColorTextureLookup(Texture2DSampleBias(Material.Texture2D_0, Material.Texture2D_0Sampler,Parameters.TexCoords.xy,View.MaterialTextureMipBias));
PixelMaterialInputs.EmissiveColor = Material.VectorExpressions.rgb;
PixelMaterialInputs.Opacty = 1.00000000;
PixelMaterialInputs.OpacityMask = 1.00000000;
PixelMaterialInputs.BaseColor = Local0.rgb;
PixelMaterialInputs.Metallic = 0.00000000;
PixelMaterialInputs.Specular = 0.50000000;
PixelMaterialInputs.Roughness = 0.50000000;
PixelMaterialInputs.Subsurface = 0;
PixelMaterialInputs.AmbientOcclusion = 1.00000000;
PixelMaterialInputs.Refraction = 0;
PixelMaterialInputs.PixelDepthOffset = 0.00000000;
PixelMaterialInputs.ShadingModel = 1;
}到这一步基本就把材质蓝图面板翻译并计算到FPixelMaterialInputs里边去了,然后就是Unreal会有一些shader模板,以移动平台为例为:MobileBasePassVertexShader.usf和MobileBasePassPixelShader.usf,这个里边就直接使用上面计算出来的变量了。这样一个完整的HLSL代码就生成出来了。
好了,大致流程清楚了,现在关键的是材质蓝图的节点以及翻译问题。这里以在材质蓝图实现一个Shader全局变量节点为例来掠一掠这个流程。
在UnrealEngine\Engine\Source\Runtime\Engine\Classes\Materials路径下新建MaterialExpressionMyGlobalVector.h,然后填写以下代码。
#pragma once
#include &#34;CoreMinimal.h&#34;
#include &#34;UObject/ObjectMacros.h&#34;
#include &#34;Materials/MaterialExpression.h&#34;
#include &#34;MaterialExpressionMyGlobalVector.generated.h&#34;
UCLASS(collapsecategories, hidecategories = Object)
class UMaterialExpressionMyGlobalVector : public UMaterialExpression
{
GENERATED_UCLASS_BODY()
//~ Begin UMaterialExpression Interface
#if WITH_EDITOR
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
virtual uint32 GetOutputType(int32 OutputIndex) override;
#endif
//~ End UMaterialExpression Interface
};这里最好编译一下,因为要生成generated.h,Unreal有个不太好的地方,编译错误就不能生成generated.h,而.h类又必须include这个generated.h,总之保证生成了MaterialExpressionMyGlobalVector.generated.h。
打开MaterialCompile.h,在FMaterialCompiler我们申明一下Complie这个节点的方法。
在FProxyMaterialCompiler这个类实现
在HLSLMaterialTranslator.h中实现这个方法
这里我们还是按照上一章的做法,放在View上,所以我们只需要添加一段这样的代码就可以了,如果对其他的翻译方法感兴趣,可以查阅这个类的其他方法。
打开MaterialExpressions.cpp,这个里边有所有的蓝图节点的实现方法,把我们的类include添加。
实现重载方法。
UMaterialExpressionMyGlobalVector::UMaterialExpressionMyGlobalVector(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
#if WITH_EDITORONLY_DATA
// Structure to hold one-time initialization
struct FConstructorStatics
{
FText NAME_Constants;
FConstructorStatics()
: NAME_Constants(LOCTEXT(&#34;Constants&#34;, &#34;Constants&#34;))
{
}
};
static FConstructorStatics ConstructorStatics;
MenuCategories.Add(ConstructorStatics.NAME_Constants);
bShaderInputData = true;
#endif
}
#if WITH_EDITOR
int32 UMaterialExpressionMyGlobalVector::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)
{
return Compiler->MyGlobalVector();
}
void UMaterialExpressionMyGlobalVector::GetCaption(TArray<FString>& OutCaptions) const
{
OutCaptions.Add(TEXT(&#34;MyGlobalVector&#34;));
}
uint32 UMaterialExpressionMyGlobalVector::GetOutputType(int32 OutputIndex)
{
return MCT_Float4;
}
#endif // WITH_EDITOR
然后在像上一章添加全局贴图的方式添加全局变量
SceneView.h
SkyLightComponent.h
SceneManagement.h
SkyLightComponent.cpp
SceneRendering.cpp
编译运行,在材质界面右键添加GlobalVector
保存材质挂到Mesh上。
在SkyLight面板,修改MyGLobalVector值,材质上的值就会实时变化。
页:
[1]