You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Add material shader quality settings. Enables quality overrides for android and ios (ES2 flavours) #codereview jack.porter [CL 2705914 by Allan Bentham in Main branch]
421 lines
14 KiB
Plaintext
421 lines
14 KiB
Plaintext
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
BasePassForForwardShadingPixelShader.usf: Base pass pixel shader used with forward shading
|
|
=============================================================================*/
|
|
|
|
#include "Common.usf"
|
|
#include "BasePassForForwardShadingCommon.usf"
|
|
#include "Material.usf"
|
|
#include "VertexFactory.usf"
|
|
#include "ReflectionEnvironmentShared.usf"
|
|
#include "LightmapCommon.usf"
|
|
#include "BRDF.usf"
|
|
#include "SHCommon.usf"
|
|
#include "ShadowFilteringCommon.usf"
|
|
#include "DynamicLightingCommon.usf"
|
|
|
|
#if MAX_DYNAMIC_POINT_LIGHTS > 0
|
|
#if VARIABLE_NUM_DYNAMIC_POINT_LIGHTS
|
|
int NumDynamicPointLights;
|
|
#endif
|
|
float4 LightPositionAndInvRadius[MAX_DYNAMIC_POINT_LIGHTS];
|
|
float4 LightColorAndFalloffExponent[MAX_DYNAMIC_POINT_LIGHTS];
|
|
#endif
|
|
|
|
#define FULLY_ROUGH (MATERIAL_FULLY_ROUGH || FORWARD_QL_FORCE_FULLY_ROUGH)
|
|
#define NONMETAL (MATERIAL_NONMETAL || FORWARD_QL_FORCE_NONMETAL)
|
|
|
|
/** Prenormalized capture of the scene that's closest to the object being rendered. */
|
|
#if !FULLY_ROUGH
|
|
TextureCube ReflectionCubemap;
|
|
SamplerState ReflectionCubemapSampler;
|
|
#endif
|
|
|
|
half PhongApprox( half Roughness, half RoL )
|
|
{
|
|
half a = Roughness * Roughness; // 1 mul
|
|
half a2 = a * a; // 1 mul
|
|
float rcp_a2 = rcp(a2); // 1 rcp
|
|
//half rcp_a2 = exp2( -6.88886882 * Roughness + 6.88886882 );
|
|
|
|
// Spherical Gaussian approximation: pow( x, n ) ~= exp( (n + 0.775) * (x - 1) )
|
|
// Phong: n = 0.5 / a2 - 0.5
|
|
// 0.5 / ln(2), 0.275 / ln(2)
|
|
half c = 0.72134752 * rcp_a2 + 0.39674113; // 1 mad
|
|
return rcp_a2 * exp2( c * RoL - c ); // 2 mad, 1 exp2, 1 mul
|
|
// Total 7 instr
|
|
}
|
|
|
|
#if !FULLY_ROUGH
|
|
half3 GetImageBasedReflectionLighting(FMaterialPixelParameters MaterialParameters, half Roughness)
|
|
{
|
|
half3 ProjectedCaptureVector = MaterialParameters.ReflectionVector;
|
|
|
|
// Compute fractional mip from roughness
|
|
half AbsoluteSpecularMip = ComputeReflectionCaptureMipFromRoughness(Roughness);
|
|
// Fetch from cubemap and convert to linear HDR
|
|
half3 SpecularIBL = RGBMDecode(ReflectionCubemap.SampleLevel(ReflectionCubemapSampler, ProjectedCaptureVector, AbsoluteSpecularMip), 16.0f);
|
|
SpecularIBL *= SpecularIBL;
|
|
#if WEBGL
|
|
// need a rgb swizzle instead of the existing rgba swizzle, we should add it if another use case comes up.
|
|
return SpecularIBL.bgr;
|
|
#else
|
|
return SpecularIBL;
|
|
#endif
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
#if USE_EDITOR_COMPOSITING
|
|
bool bEnableEditorPrimitiveDepthTest;
|
|
// depth in the red channel in DeviceZ
|
|
Texture2D FilteredSceneDepthTexture;
|
|
SamplerState FilteredSceneDepthTextureSampler;
|
|
#endif
|
|
|
|
|
|
#if USE_EDITOR_COMPOSITING && (MOBILE_EMULATION)
|
|
// @return 0:translucent..1:opaque
|
|
float ClipForEditorPrimitives(FMaterialPixelParameters MaterialParameters)
|
|
{
|
|
float Ret = 1;
|
|
|
|
// Depth test manually if compositing editor primitives since the depth buffer is different (MSAA only)
|
|
BRANCH
|
|
if (bEnableEditorPrimitiveDepthTest)
|
|
{
|
|
bool bIsPerspective = (View.ViewToClip._m33 < 1.0f);
|
|
|
|
// dejitter the sample position and make a filtered lookup - for planes this allows to reconstruct a much less jittery depth comparison function, it however doesn't fix silhuetes
|
|
float DeviceZ = Texture2DSampleLevel(FilteredSceneDepthTexture, FilteredSceneDepthTextureSampler, (MaterialParameters.SvPosition.xy - View.TemporalAAParams.zw) * View.BufferSizeAndInvSize.zw, 0).r;
|
|
|
|
float PixelDeviceZ = MaterialParameters.SvPosition.z;
|
|
|
|
// Soft Bias with DeviceZ for best quality
|
|
const float DeviceDepthFade = 0.00005f;
|
|
|
|
// 0.5f is to bias around the actual value, 1 or 0 are another option
|
|
Ret = saturate(0.5f - (DeviceZ - PixelDeviceZ) / DeviceDepthFade);
|
|
}
|
|
|
|
// Note: multiple returns cause strange HLSL compiler error for CV_Coverage in later code
|
|
return Ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
half3 FrameBufferBlendOp(half4 Source)
|
|
{
|
|
#if (COMPILER_GLSL_ES2)
|
|
half4 Dest = FramebufferFetchES2();
|
|
Dest.rgb = Decode32BPPHDR(Dest);
|
|
#else
|
|
half4 Dest = half4 (0,0,0,0);
|
|
#endif
|
|
|
|
#if MATERIALBLENDING_SOLID
|
|
return Source.rgb;
|
|
#elif MATERIALBLENDING_MASKED
|
|
return Source.rgb;
|
|
#elif MATERIALBLENDING_TRANSLUCENT
|
|
return (Source.rgb*Source.a) + (Dest.rgb*(1.0 - Source.a));
|
|
#elif MATERIALBLENDING_ADDITIVE
|
|
return Source.rgb + Dest.rgb;
|
|
#elif MATERIALBLENDING_MODULATE
|
|
return Source.rgb * Dest.rgb;
|
|
#endif
|
|
}
|
|
|
|
half4 IndirectLightingSHCoefficients[3];
|
|
half DirectionalLightShadowing;
|
|
|
|
void Main(
|
|
FVertexFactoryInterpolantsVSToPS Interpolants,
|
|
FForwardShadingBasePassInterpolantsVSToPS BasePassInterpolants,
|
|
in float4 SvPosition : SV_Position
|
|
OPTIONAL_IsFrontFace,
|
|
out half4 OutColor : SV_Target0
|
|
)
|
|
{
|
|
#if PACK_INTERPOLANTS
|
|
float4 PackedInterpolants[NUM_VF_PACKED_INTERPOLANTS];
|
|
VertexFactoryUnpackInterpolants(Interpolants, PackedInterpolants);
|
|
#endif
|
|
FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(Interpolants, SvPosition);
|
|
|
|
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS
|
|
{
|
|
float4 ScreenPosition = SvPositionToScreenPosition(SvPosition);
|
|
CalcMaterialParametersEx(MaterialParameters, SvPosition, ScreenPosition, bIsFrontFace, SvPositionToTranslatedWorld(SvPosition), BasePassInterpolants.PixelPositionExcludingWPO);
|
|
}
|
|
#else
|
|
CalcMaterialParameters(MaterialParameters, SvPosition, bIsFrontFace);
|
|
#endif
|
|
|
|
//Clip if the blend mode requires it.
|
|
GetMaterialCoverageAndClipping(MaterialParameters);
|
|
|
|
// Store the results in local variables and reuse instead of calling the functions multiple times.
|
|
// Store the results in local variables and reuse instead of calling the functions multiple times.
|
|
half3 BaseColor = GetMaterialBaseColor( MaterialParameters );
|
|
half Metallic = GetMaterialMetallic( MaterialParameters );
|
|
half Specular = GetMaterialSpecular( MaterialParameters );
|
|
|
|
#if NONMETAL
|
|
half3 DiffuseColor = BaseColor;
|
|
half SpecularColor = 0.04;
|
|
#else
|
|
half DielectricSpecular = 0.08 * Specular;
|
|
half3 DiffuseColor = BaseColor - BaseColor * Metallic; // 1 mad
|
|
half3 SpecularColor = (DielectricSpecular - DielectricSpecular * Metallic) + BaseColor * Metallic; // 2 mad
|
|
#endif
|
|
|
|
half Roughness = GetMaterialRoughness(MaterialParameters);
|
|
|
|
#if FULLY_ROUGH
|
|
// Factors derived from EnvBRDFApprox( SpecularColor, 1, 1 ) == SpecularColor * 0.4524 - 0.0024
|
|
DiffuseColor += SpecularColor * 0.45;
|
|
SpecularColor = 0;
|
|
#else
|
|
half NoV = max( dot( MaterialParameters.WorldNormal, MaterialParameters.CameraVector ), 0 );
|
|
|
|
#if NONMETAL
|
|
// If nothing is hooked up to Metalic and Specular,
|
|
// then defaults are the same as a non-metal,
|
|
// so this define is safe.
|
|
SpecularColor = EnvBRDFApproxNonmetal( Roughness, NoV );
|
|
#else
|
|
SpecularColor = EnvBRDFApprox( SpecularColor, Roughness, NoV );
|
|
#endif
|
|
#endif
|
|
|
|
half3 Color = 0;
|
|
half IndirectIrradiance = 0;
|
|
|
|
#if LQ_TEXTURE_LIGHTMAP
|
|
float2 LightmapUV0, LightmapUV1;
|
|
GetLightMapCoordinates(Interpolants, LightmapUV0, LightmapUV1);
|
|
|
|
half4 LightmapColor = GetLightMapColorLQ( LightmapUV0, LightmapUV1, MaterialParameters.WorldNormal );
|
|
Color += LightmapColor.rgb * DiffuseColor;
|
|
IndirectIrradiance = LightmapColor.a;
|
|
#elif CACHED_POINT_INDIRECT_LIGHTING
|
|
#if MATERIALBLENDING_MASKED || MATERIALBLENDING_SOLID
|
|
|
|
// Take the normal into account for opaque
|
|
FTwoBandSHVectorRGB PointIndirectLighting;
|
|
PointIndirectLighting.R.V = IndirectLightingSHCoefficients[0];
|
|
PointIndirectLighting.G.V = IndirectLightingSHCoefficients[1];
|
|
PointIndirectLighting.B.V = IndirectLightingSHCoefficients[2];
|
|
|
|
FTwoBandSHVector DiffuseTransferSH = CalcDiffuseTransferSH(MaterialParameters.WorldNormal, 1);
|
|
|
|
// Compute diffuse lighting which takes the normal into account
|
|
half3 DiffuseGI = max(half3(0,0,0), DotSH(PointIndirectLighting, DiffuseTransferSH));
|
|
|
|
IndirectIrradiance = Luminance(DiffuseGI);
|
|
Color += DiffuseColor * DiffuseGI;
|
|
|
|
#else
|
|
|
|
// Non-directional for translucency
|
|
// Ambient terms packed in xyz
|
|
// Already divided by PI and SH ambient on CPU
|
|
half3 PointIndirectLighting = IndirectLightingSHCoefficients[0];
|
|
half3 DiffuseGI = PointIndirectLighting;
|
|
|
|
IndirectIrradiance = Luminance(DiffuseGI);
|
|
Color += DiffuseColor * DiffuseGI;
|
|
|
|
#endif
|
|
#endif
|
|
|
|
#if !MATERIAL_SHADINGMODEL_UNLIT
|
|
half Shadow = GetPrimaryPrecomputedShadowMask(Interpolants).r;
|
|
|
|
#if SIMPLE_DYNAMIC_LIGHTING
|
|
Shadow = DirectionalLightShadowing;
|
|
#endif
|
|
|
|
#if MOVABLE_DIRECTIONAL_LIGHT_CSM
|
|
// Cascaded Shadow Map
|
|
{
|
|
FPCFSamplerSettings Settings;
|
|
Settings.ShadowDepthTexture = View.DirectionalLightShadowTexture;
|
|
Settings.ShadowDepthTextureSampler = View.DirectionalLightShadowSampler;
|
|
Settings.TransitionScale = View.DirectionalLightShadowTransition;
|
|
Settings.ShadowBufferSize = View.DirectionalLightShadowSize;
|
|
Settings.bSubsurface = false;
|
|
Settings.DensityMulConstant = 0;
|
|
Settings.ProjectionDepthBiasParameters = 0;
|
|
|
|
float4 ShadowPosition;
|
|
for (int i = 0; i < MAX_FORWARD_SHADOWCASCADES; i++)
|
|
{
|
|
if (i == (MAX_FORWARD_SHADOWCASCADES - 1) // ignore test for last shadow map.
|
|
|| MaterialParameters.ScreenPosition.w < View.DirectionalLightShadowDistances[i])
|
|
{
|
|
ShadowPosition = mul(float4(MaterialParameters.ScreenPosition.xyw, 1), View.DirectionalLightScreenToShadow[i]);
|
|
break; // position found.
|
|
}
|
|
}
|
|
|
|
// Clamp pixel depth in light space for shadowing opaque, because areas of the shadow depth buffer that weren't rendered to will have been cleared to 1
|
|
// We want to force the shadow comparison to result in 'unshadowed' in that case, regardless of whether the pixel being shaded is in front or behind that plane
|
|
float LightSpacePixelDepthForOpaque = min(ShadowPosition.z, 0.99999f);
|
|
Settings.SceneDepth = LightSpacePixelDepthForOpaque;
|
|
|
|
#if 0
|
|
half ShadowMap = Manual1x1PCF(ShadowPosition.xy, Settings);
|
|
#else
|
|
half ShadowMap = Manual2x2PCF(ShadowPosition.xy, Settings);
|
|
#endif
|
|
Shadow = ShadowMap;
|
|
}
|
|
#endif /* MOVABLE_DIRECTIONAL_LIGHT_CSM */
|
|
|
|
float NoL = max(0, dot(MaterialParameters.WorldNormal, View.DirectionalLightDirection));
|
|
float RoL = max(0, dot(MaterialParameters.ReflectionVector, View.DirectionalLightDirection));
|
|
|
|
#if FULLY_ROUGH
|
|
Color += (Shadow * NoL) * View.DirectionalLightColor.rgb * DiffuseColor;
|
|
#else
|
|
Color += (Shadow * NoL) * View.DirectionalLightColor.rgb * (DiffuseColor + SpecularColor * PhongApprox(Roughness, RoL));
|
|
half3 SpecularIBL = GetImageBasedReflectionLighting(MaterialParameters, Roughness);
|
|
// Environment map has been prenormalized, scale by lightmap luminance
|
|
Color += SpecularIBL * IndirectIrradiance * SpecularColor;
|
|
#endif
|
|
|
|
#if ENABLE_SKY_LIGHT
|
|
//@mw todo
|
|
// TODO: Also need to do specular.
|
|
Color += GetSkySHDiffuseSimple(MaterialParameters.WorldNormal) * View.SkyLightColor.rgb * DiffuseColor;
|
|
#endif
|
|
|
|
#if MAX_DYNAMIC_POINT_LIGHTS > 0
|
|
#if VARIABLE_NUM_DYNAMIC_POINT_LIGHTS
|
|
#if WEBGL
|
|
// webgl needs a constant loop counter
|
|
UNROLL
|
|
for (int i = 0; i < MAX_DYNAMIC_POINT_LIGHTS; i++)
|
|
{
|
|
if(i >= NumDynamicPointLights)
|
|
{
|
|
break;
|
|
}
|
|
#else
|
|
for (int i = 0; i < NumDynamicPointLights; i++)
|
|
{
|
|
#endif
|
|
#else
|
|
for (int i = 0; i < NUM_DYNAMIC_POINT_LIGHTS; i++)
|
|
{
|
|
#endif
|
|
float3 ToLight = LightPositionAndInvRadius[i].xyz - MaterialParameters.AbsoluteWorldPosition;
|
|
float DistanceSqr = dot(ToLight, ToLight);
|
|
float3 L = ToLight * rsqrt(DistanceSqr);
|
|
|
|
float PointNoL = max(0, dot(MaterialParameters.WorldNormal, L));
|
|
float PointRoL = max(0, dot(MaterialParameters.ReflectionVector, L));
|
|
|
|
float Attenuation;
|
|
|
|
if (LightColorAndFalloffExponent[i].w == 0)
|
|
{
|
|
// Sphere falloff (technically just 1/d2 but this avoids inf)
|
|
Attenuation = 1 / ( DistanceSqr + 1 );
|
|
|
|
float LightRadiusMask = Square(saturate(1 - Square(DistanceSqr * (LightPositionAndInvRadius[i].w * LightPositionAndInvRadius[i].w))));
|
|
Attenuation *= LightRadiusMask;
|
|
}
|
|
else
|
|
{
|
|
Attenuation = RadialAttenuation(ToLight * LightPositionAndInvRadius[i].w, LightColorAndFalloffExponent[i].w);
|
|
}
|
|
|
|
#if !FULLY_ROUGH
|
|
Color += (Attenuation * PointNoL) * LightColorAndFalloffExponent[i].rgb * (DiffuseColor + SpecularColor * PhongApprox(Roughness, PointRoL));
|
|
#else
|
|
Color += (Attenuation * PointNoL) * LightColorAndFalloffExponent[i].rgb * DiffuseColor;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#endif /* !MATERIAL_SHADINGMODEL_UNLIT */
|
|
|
|
half Opacity = GetMaterialOpacity(MaterialParameters);
|
|
half3 Emissive = GetMaterialEmissive(MaterialParameters);
|
|
|
|
Color += Emissive;
|
|
|
|
half4 VertexFog = half4(0, 0, 0, 1);
|
|
#if USE_VERTEX_FOG
|
|
#if PACK_INTERPOLANTS
|
|
VertexFog = PackedInterpolants[0];
|
|
#else
|
|
VertexFog = BasePassInterpolants.VertexFog;
|
|
#endif
|
|
#endif
|
|
|
|
#if !MATERIAL_SHADINGMODEL_UNLIT && MOBILE_EMULATION
|
|
Color = lerp(Color, DiffuseColor + SpecularColor, View.UnlitViewmodeMask);
|
|
#endif
|
|
|
|
#if MATERIALBLENDING_TRANSLUCENT
|
|
OutColor = half4(Color * VertexFog.a + VertexFog.rgb, Opacity);
|
|
#elif MATERIALBLENDING_ADDITIVE
|
|
OutColor = half4(Color * (VertexFog.a * Opacity.x), 0.0f);
|
|
#elif MATERIALBLENDING_MODULATE
|
|
half3 FoggedColor = lerp(half3(1, 1, 1), Color, VertexFog.aaa * VertexFog.aaa);
|
|
OutColor = half4(FoggedColor, Opacity);
|
|
#else
|
|
OutColor.rgb = Color * VertexFog.a + VertexFog.rgb;
|
|
|
|
// Scene color alpha is not used yet so we set it to 0
|
|
OutColor.a = 0.0;
|
|
|
|
#if ES2_PROFILE && !OUTPUT_GAMMA_SPACE
|
|
if(GetHDR32bppEncodeMode() == HDR_ENCODE_NONE)
|
|
{
|
|
// FP16 HDR stores Z in framebuffer alpha
|
|
OutColor.a = MaterialParameters.ScreenPosition.w;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !MATERIALBLENDING_MODULATE
|
|
// The exposure scale is just a scalar but needs to be a float4 to workaround a driver bug on IOS.
|
|
// After 4.2 we can put the workaround in the cross compiler.
|
|
OutColor.rgba *= View.ExposureScale.xyzw;
|
|
#endif
|
|
|
|
#if OUTPUT_GAMMA_SPACE
|
|
OutColor.rgb = sqrt( OutColor.rgb );
|
|
#endif
|
|
|
|
#if ES2_PROFILE && !OUTPUT_GAMMA_SPACE
|
|
float Mode = GetHDR32bppEncodeMode();
|
|
if (Mode != HDR_ENCODE_NONE)
|
|
{
|
|
if (Mode == HDR_ENCODE_RGBA)
|
|
{
|
|
OutColor.rgb = FrameBufferBlendOp(OutColor);
|
|
}
|
|
OutColor = Encode32BPPHDR(OutColor, SvPosition.xy);
|
|
}
|
|
#endif
|
|
|
|
// Editor primitive depth testing
|
|
#if USE_EDITOR_COMPOSITING && (MOBILE_EMULATION)
|
|
#if MATERIALBLENDING_MASKED
|
|
// some material might have a opacity value
|
|
OutColor.a = GetMaterialMaskInputRaw(MaterialParameters);
|
|
#endif
|
|
// we output premultiplied alpha to we have to darken all 4 channels
|
|
OutColor *= ClipForEditorPrimitives(MaterialParameters);
|
|
clip(OutColor.a - GetMaterialOpacityMaskClipValue());
|
|
#endif
|
|
}
|