Files
UnrealEngineUWP/Engine/Shaders/BasePassForForwardShadingPixelShader.usf
Allan Bentham 25c10bf77d UEMOB-36
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]
2015-09-25 04:55:45 -04:00

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
}