Skip to content

Instantly share code, notes, and snippets.

@untodesu
Created May 21, 2021 08:50
Show Gist options
  • Save untodesu/a42e29b34c76dc4c0035f81a921c6fe3 to your computer and use it in GitHub Desktop.
Save untodesu/a42e29b34c76dc4c0035f81a921c6fe3 to your computer and use it in GitHub Desktop.
// PBR shader.
// Originates from https://github.com/thexa4/source-pbr
// Adopted for Refraction. Things done so far:
// 1. Fixed SP branch crash (See PSREG_FREE).
// 2. Removed all the verbosity in the comments
// leaving only stuff that explains obfuscated stuff.
#include "BaseVSShader.h"
#include "cpp_shader_constant_register_map.h"
#include "pbr_vs30.inc"
#include "pbr_ps30.inc"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
const Sampler_t SAMPLER_BASETEXTURE = SHADER_SAMPLER0;
const Sampler_t SAMPLER_NORMAL = SHADER_SAMPLER1;
const Sampler_t SAMPLER_ENVMAP = SHADER_SAMPLER2;
const Sampler_t SAMPLER_SHADOWDEPTH = SHADER_SAMPLER4;
const Sampler_t SAMPLER_RANDOMROTATION = SHADER_SAMPLER5;
const Sampler_t SAMPLER_FLASHLIGHT = SHADER_SAMPLER6;
const Sampler_t SAMPLER_LIGHTMAP = SHADER_SAMPLER7;
const Sampler_t SAMPLER_MRAO = SHADER_SAMPLER10;
const Sampler_t SAMPLER_EMISSIVE = SHADER_SAMPLER11;
const Sampler_t SAMPLER_SPECULAR = SHADER_SAMPLER12;
static ConVarRef mat_fullbright( "mat_fullbright", false );
static ConVarRef mat_specular( "mat_specular", false );
static ConVar mat_pbr_parallaxmap( "mat_pbr_parallaxmap", "1" );
struct PBR_Vars_t {
PBR_Vars_t()
{
memset( this, 0xFF, sizeof( *this ) );
}
int baseTexture;
int baseColor;
int bumpMap;
int envMap;
int baseTextureFrame;
int baseTextureTransform;
int useParallax;
int parallaxDepth;
int parallaxCenter;
int alphaTestReference;
int flashlightTexture;
int flashlightTextureFrame;
int emissionTexture;
int mraoTexture;
int useEnvAmbient;
int specularTexture;
};
BEGIN_VS_SHADER( PBR, "Help for PBR shader" )
BEGIN_SHADER_PARAMS;
SHADER_PARAM( ALPHATESTREFERENCE, SHADER_PARAM_TYPE_FLOAT, "0", "" )
SHADER_PARAM( ENVMAP, SHADER_PARAM_TYPE_ENVMAP, "", "" )
SHADER_PARAM( MRAOTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "" )
SHADER_PARAM( EMISSIONTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "" )
SHADER_PARAM( BUMPMAP, SHADER_PARAM_TYPE_TEXTURE, "", "" )
SHADER_PARAM( USEENVAMBIENT, SHADER_PARAM_TYPE_BOOL, "0", "" )
SHADER_PARAM( SPECULARTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "" )
SHADER_PARAM( PARALLAX, SHADER_PARAM_TYPE_BOOL, "0", "" )
SHADER_PARAM( PARALLAXDEPTH, SHADER_PARAM_TYPE_FLOAT, "0.0030", "" )
SHADER_PARAM( PARALLAXCENTER, SHADER_PARAM_TYPE_FLOAT, "0.5", "" )
END_SHADER_PARAMS;
void SetupVars( PBR_Vars_t &info )
{
info.baseTexture = BASETEXTURE;
info.baseColor = COLOR;
info.bumpMap = BUMPMAP;
info.baseTextureFrame = FRAME;
info.baseTextureTransform = BASETEXTURETRANSFORM;
info.alphaTestReference = ALPHATESTREFERENCE;
info.flashlightTexture = FLASHLIGHTTEXTURE;
info.flashlightTextureFrame = FLASHLIGHTTEXTUREFRAME;
info.envMap = ENVMAP;
info.emissionTexture = EMISSIONTEXTURE;
info.mraoTexture = MRAOTEXTURE;
info.useEnvAmbient = USEENVAMBIENT;
info.specularTexture = SPECULARTEXTURE;
info.useParallax = PARALLAX;
info.parallaxDepth = PARALLAXDEPTH;
info.parallaxCenter = PARALLAXCENTER;
}
SHADER_FALLBACK
{
return 0;
}
SHADER_INIT
{
if( !params[BUMPMAP]->IsDefined() )
params[BUMPMAP]->SetStringValue( "dev/flat_normal" );
if( !params[MRAOTEXTURE]->IsDefined() )
params[MRAOTEXTURE]->SetStringValue( "dev/pbr_mraotexture" );
if( !params[ENVMAP]->IsDefined() )
params[ENVMAP]->SetStringValue( "env_cubemap" );
if( g_pHardwareConfig->SupportsBorderColor() )
params[FLASHLIGHTTEXTURE]->SetStringValue( "effects/flashlight_border" );
else
params[FLASHLIGHTTEXTURE]->SetStringValue( "effects/flashlight001" );
PBR_Vars_t info;
SetupVars( info );
LoadTexture( info.flashlightTexture, TEXTUREFLAGS_SRGB );
LoadBumpMap( info.bumpMap );
LoadCubeMap( info.envMap, ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ? TEXTUREFLAGS_SRGB : 0 ) | TEXTUREFLAGS_ALL_MIPS );
LoadTexture( info.mraoTexture, 0 );
if( info.emissionTexture >= 0 && params[EMISSIONTEXTURE]->IsDefined() )
LoadTexture( info.emissionTexture, TEXTUREFLAGS_SRGB );
if( params[info.baseTexture]->IsDefined() )
LoadTexture( info.baseTexture, TEXTUREFLAGS_SRGB );
if( params[info.specularTexture]->IsDefined() )
LoadTexture( info.specularTexture, TEXTUREFLAGS_SRGB );
if( IS_FLAG_SET( MATERIAL_VAR_MODEL ) ) {
SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_HW_SKINNING ); // Required for skinning
SET_FLAGS2( MATERIAL_VAR2_DIFFUSE_BUMPMAPPED_MODEL ); // Required for dynamic lighting
SET_FLAGS2( MATERIAL_VAR2_NEEDS_TANGENT_SPACES ); // Required for dynamic lighting
SET_FLAGS2( MATERIAL_VAR2_LIGHTING_VERTEX_LIT ); // Required for dynamic lighting
SET_FLAGS2( MATERIAL_VAR2_NEEDS_BAKED_LIGHTING_SNAPSHOTS ); // Required for ambient cube
SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_FLASHLIGHT ); // Required for flashlight
SET_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); // Required for flashlight
}
else {
SET_FLAGS2( MATERIAL_VAR2_LIGHTING_LIGHTMAP ); // Required for lightmaps
SET_FLAGS2( MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ); // Required for lightmaps
SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_FLASHLIGHT ); // Required for flashlight
SET_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); // Required for flashlight
}
}
SHADER_DRAW
{
PBR_Vars_t info;
SetupVars( info );
const bool bAlphaTested = IS_FLAG_SET( MATERIAL_VAR_ALPHATEST );
const bool bLightmapped = !IS_FLAG_SET( MATERIAL_VAR_MODEL );
const bool bHasBaseTexture = params[info.baseTexture]->IsTexture();
const bool bHasBumpTexture = params[info.bumpMap]->IsTexture();
const bool bHasMRAOTexture = params[info.mraoTexture]->IsTexture();
const bool bHasEmissionTexture = params[info.emissionTexture]->IsTexture();
const bool bHasSpecularTexture = params[info.specularTexture]->IsTexture();
const bool bHasEnvMapTexture = params[info.envMap]->IsTexture();
const bool bUsingFlashlight = UsingFlashlight( params );
const bool bHasColor = params[info.baseColor]->IsDefined() && info.baseColor != -1;
const bool bUseEnvAmbient = !!params[info.useEnvAmbient]->GetIntValue();
BlendType_t nBlendType = EvaluateBlendRequirements( info.baseTexture, true );
bool bFullyOpaque = ( nBlendType != BT_BLENDADD ) && ( nBlendType != BT_BLEND ) && !bAlphaTested;
SHADOW_STATE
{
pShaderShadow->EnableAlphaTest( bAlphaTested );
if( info.alphaTestReference != -1 && params[info.alphaTestReference]->GetFloatValue() > 0.0f ) {
pShaderShadow->AlphaFunc( SHADER_ALPHAFUNC_GEQUAL, params[info.alphaTestReference]->GetFloatValue() );
}
if( bUsingFlashlight ) {
// Additive blending
pShaderShadow->EnableBlending( true );
pShaderShadow->BlendFunc( SHADER_BLEND_ONE, SHADER_BLEND_ONE );
}
else
SetDefaultBlendingShadowState( info.baseTexture, true );
int nShadowFilterMode = bUsingFlashlight ? g_pHardwareConfig->GetShadowFilterMode() : 0;
pShaderShadow->EnableTexture( SAMPLER_BASETEXTURE, true );
pShaderShadow->EnableSRGBRead( SAMPLER_BASETEXTURE, true );
pShaderShadow->EnableTexture( SAMPLER_EMISSIVE, true );
pShaderShadow->EnableSRGBRead( SAMPLER_EMISSIVE, true );
pShaderShadow->EnableTexture( SAMPLER_LIGHTMAP, true );
pShaderShadow->EnableSRGBRead( SAMPLER_LIGHTMAP, false );
pShaderShadow->EnableTexture( SAMPLER_MRAO, true );
pShaderShadow->EnableSRGBRead( SAMPLER_MRAO, false );
pShaderShadow->EnableTexture( SAMPLER_NORMAL, true );
pShaderShadow->EnableSRGBRead( SAMPLER_NORMAL, false );
pShaderShadow->EnableTexture( SAMPLER_SPECULAR, true );
pShaderShadow->EnableSRGBRead( SAMPLER_SPECULAR, true );
if( bUsingFlashlight ) {
pShaderShadow->EnableTexture( SAMPLER_SHADOWDEPTH, true );
pShaderShadow->SetShadowDepthFiltering( SAMPLER_SHADOWDEPTH );
pShaderShadow->EnableSRGBRead( SAMPLER_SHADOWDEPTH, false );
pShaderShadow->EnableTexture( SAMPLER_RANDOMROTATION, true );
pShaderShadow->EnableTexture( SAMPLER_FLASHLIGHT, true );
pShaderShadow->EnableSRGBRead( SAMPLER_FLASHLIGHT, true );
}
if( bHasEnvMapTexture ) {
pShaderShadow->EnableTexture( SAMPLER_ENVMAP, true );
if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE )
pShaderShadow->EnableSRGBRead( SAMPLER_ENVMAP, true );
}
// PS2b shaders and up write sRGB (See common_ps_fxc.h line 349)
// und: is it true?
pShaderShadow->EnableSRGBWrite( true );
if( IS_FLAG_SET( MATERIAL_VAR_MODEL ) ) {
unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_COMPRESSED;
pShaderShadow->VertexShaderVertexFormat( flags, 1, 0, 0 );
}
else {
unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL;
pShaderShadow->VertexShaderVertexFormat( flags, 3, 0, 0 );
}
int useParallax = params[info.useParallax]->GetIntValue();
if( !mat_pbr_parallaxmap.GetBool() ) {
useParallax = 0;
}
DECLARE_STATIC_VERTEX_SHADER( pbr_vs30 );
SET_STATIC_VERTEX_SHADER( pbr_vs30 );
DECLARE_STATIC_PIXEL_SHADER( pbr_ps30 );
SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bUsingFlashlight );
SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode );
SET_STATIC_PIXEL_SHADER_COMBO( LIGHTMAPPED, bLightmapped );
SET_STATIC_PIXEL_SHADER_COMBO( USEENVAMBIENT, bUseEnvAmbient );
SET_STATIC_PIXEL_SHADER_COMBO( EMISSIVE, bHasEmissionTexture );
SET_STATIC_PIXEL_SHADER_COMBO( SPECULAR, bHasSpecularTexture );
SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXOCCLUSION, useParallax );
SET_STATIC_PIXEL_SHADER( pbr_ps30 );
// I think this is correct
DefaultFog();
pShaderShadow->EnableAlphaWrites( bFullyOpaque );
}
DYNAMIC_STATE
{
bool bLightingOnly = mat_fullbright.GetInt() == 2 && !IS_FLAG_SET( MATERIAL_VAR_NO_DEBUG_OVERRIDE );
if( bHasBaseTexture )
BindTexture( SAMPLER_BASETEXTURE, info.baseTexture, info.baseTextureFrame );
else
pShaderAPI->BindStandardTexture( SAMPLER_BASETEXTURE, TEXTURE_GREY );
Vector color;
if( bHasColor )
params[info.baseColor]->GetVecValue( color.Base(), 3 );
else
color = Vector{ 1.f, 1.f, 1.f };
pShaderAPI->SetPixelShaderConstant( PSREG_SELFILLUMTINT, color.Base() );
if( bHasEnvMapTexture )
BindTexture( SAMPLER_ENVMAP, info.envMap, 0 );
else
pShaderAPI->BindStandardTexture( SAMPLER_ENVMAP, TEXTURE_BLACK );
if( bHasEmissionTexture )
BindTexture( SAMPLER_EMISSIVE, info.emissionTexture, 0 );
else
pShaderAPI->BindStandardTexture( SAMPLER_EMISSIVE, TEXTURE_BLACK );
if( bHasBumpTexture )
BindTexture( SAMPLER_NORMAL, info.bumpMap, 0 );
else
pShaderAPI->BindStandardTexture( SAMPLER_NORMAL, TEXTURE_NORMALMAP_FLAT );
if( bHasMRAOTexture )
BindTexture( SAMPLER_MRAO, info.mraoTexture, 0 );
else
pShaderAPI->BindStandardTexture( SAMPLER_MRAO, TEXTURE_WHITE );
if( bHasSpecularTexture )
BindTexture( SAMPLER_SPECULAR, info.specularTexture, 0 );
else
pShaderAPI->BindStandardTexture( SAMPLER_SPECULAR, TEXTURE_BLACK );
LightState_t lightState;
pShaderAPI->GetDX9LightState( &lightState );
if( !IS_FLAG_SET( MATERIAL_VAR_MODEL ) ) {
lightState.m_bAmbientLight = false;
lightState.m_nNumLights = 0;
}
bool bFlashlightShadows = false;
if( bUsingFlashlight ) {
BindTexture( SAMPLER_FLASHLIGHT, info.flashlightTexture, info.flashlightTextureFrame );
VMatrix worldToTexture;
ITexture *pFlashlightDepthTexture;
FlashlightState_t state = pShaderAPI->GetFlashlightStateEx( worldToTexture, &pFlashlightDepthTexture );
bFlashlightShadows = state.m_bEnableShadows && pFlashlightDepthTexture;
SetFlashLightColorFromState( state, pShaderAPI, PSREG_FLASHLIGHT_COLOR );
if( pFlashlightDepthTexture && g_pConfig->ShadowDepthTexture() && state.m_bEnableShadows ) {
BindTexture( SAMPLER_SHADOWDEPTH, pFlashlightDepthTexture, 0 );
pShaderAPI->BindStandardTexture( SAMPLER_RANDOMROTATION, TEXTURE_SHADOW_NOISE_2D );
}
}
MaterialFogMode_t fogType = pShaderAPI->GetSceneFogMode();
int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0;
int numBones = pShaderAPI->GetCurrentNumBones();
bool bWriteDepthToAlpha = false;
bool bWriteWaterFogToAlpha = false;
if( bFullyOpaque ) {
bWriteDepthToAlpha = pShaderAPI->ShouldWriteDepthToDestAlpha();
bWriteWaterFogToAlpha = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z );
}
float vEyePos_SpecExponent[4];
pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent );
int iEnvMapLOD = 6;
auto envTexture = params[info.envMap]->GetTextureValue();
if( envTexture ) {
int width = envTexture->GetMappingWidth();
int mips = 0;
while( width >>= 1 ) mips++;
iEnvMapLOD = mips;
}
iEnvMapLOD = Clamp( iEnvMapLOD, 4, 12 );
// vEyePos_SpecExponent has some spare space
vEyePos_SpecExponent[3] = iEnvMapLOD;
pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 );
s_pShaderAPI->BindStandardTexture( SAMPLER_LIGHTMAP, TEXTURE_LIGHTMAP_BUMPED );
DECLARE_DYNAMIC_VERTEX_SHADER( pbr_vs30 );
SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex );
SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 );
SET_DYNAMIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter( INT_RENDERPARM_ENABLE_FIXED_LIGHTING ) != 0 );
SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression );
SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights );
SET_DYNAMIC_VERTEX_SHADER( pbr_vs30 );
DECLARE_DYNAMIC_PIXEL_SHADER( pbr_ps30 );
SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights );
SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha );
SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bWriteDepthToAlpha );
SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows );
SET_DYNAMIC_PIXEL_SHADER( pbr_ps30 );
SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, info.baseTextureTransform );
// This is probably important
SetModulationPixelShaderDynamicState_LinearColorSpace( 1 );
pShaderAPI->SetPixelShaderStateAmbientLightCube( PSREG_AMBIENT_CUBE, !lightState.m_bAmbientLight );
pShaderAPI->CommitPixelShaderLighting( PSREG_LIGHT_INFO_ARRAY );
// Handle mat_fullbright 2 (diffuse lighting only)
if( bLightingOnly )
pShaderAPI->BindStandardTexture( SAMPLER_BASETEXTURE, TEXTURE_GREY );
// Handle mat_specular 0 (no envmap reflections)
if( !mat_specular.GetBool() )
pShaderAPI->BindStandardTexture( SAMPLER_ENVMAP, TEXTURE_BLACK );
pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS );
float modulationColor[4] = { 1.0, 1.0, 1.0, 1.0 };
ComputeModulationColor( modulationColor );
float flLScale = pShaderAPI->GetLightMapScaleFactor();
modulationColor[0] *= flLScale;
modulationColor[1] *= flLScale;
modulationColor[2] *= flLScale;
pShaderAPI->SetPixelShaderConstant( PSREG_DIFFUSE_MODULATION, modulationColor );
if( bUsingFlashlight ) {
VMatrix worldToTexture;
float atten[4], pos[4], tweaks[4];
const FlashlightState_t &flashlightState = pShaderAPI->GetFlashlightState( worldToTexture );
SetFlashLightColorFromState( flashlightState, pShaderAPI, PSREG_FLASHLIGHT_COLOR );
BindTexture( SAMPLER_FLASHLIGHT, flashlightState.m_pSpotlightTexture, flashlightState.m_nSpotlightTextureFrame );
atten[0] = flashlightState.m_fConstantAtten;
atten[1] = flashlightState.m_fLinearAtten;
atten[2] = flashlightState.m_fQuadraticAtten;
atten[3] = flashlightState.m_FarZ;
pShaderAPI->SetPixelShaderConstant( PSREG_FLASHLIGHT_ATTENUATION, atten, 1 );
pos[0] = flashlightState.m_vecLightOrigin[0];
pos[1] = flashlightState.m_vecLightOrigin[1];
pos[2] = flashlightState.m_vecLightOrigin[2];
pShaderAPI->SetPixelShaderConstant( PSREG_FLASHLIGHT_POSITION_RIM_BOOST, pos, 1 );
pShaderAPI->SetPixelShaderConstant( PSREG_FLASHLIGHT_TO_WORLD_TEXTURE, worldToTexture.Base(), 4 );
tweaks[0] = ShadowFilterFromState( flashlightState );
tweaks[1] = ShadowAttenFromState( flashlightState );
HashShadow2DJitter( flashlightState.m_flShadowJitterSeed, &tweaks[2], &tweaks[3] );
pShaderAPI->SetPixelShaderConstant( PSREG_ENVMAP_TINT__SHADOW_TWEAKS, tweaks, 1 );
}
float flParams[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
// Parallax Depth (the strength of the effect)
flParams[0] = GetFloatParam( info.parallaxDepth, params, 3.0f );
// Parallax Center (the height at which it's not moved)
flParams[1] = GetFloatParam( info.parallaxCenter, params, 3.0f );
// und: used PSREG_FREE (c27) instead of hardcoded c40.
// SP somehow does not support more than 32 (c31) constants. Too bad!
pShaderAPI->SetPixelShaderConstant( PSREG_FREE, flParams, 1 );
}
Draw();
}
END_SHADER
// STATIC: "FLASHLIGHT" "0..1"
// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2"
// STATIC: "LIGHTMAPPED" "0..1"
// STATIC: "USEENVAMBIENT" "0..1"
// STATIC: "EMISSIVE" "0..1"
// STATIC: "SPECULAR" "0..1"
// STATIC: "PARALLAXOCCLUSION" "0..1"
// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1"
// DYNAMIC: "PIXELFOGTYPE" "0..1"
// DYNAMIC: "NUM_LIGHTS" "0..4"
// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1"
// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1"
// Can't write fog to alpha if there is no fog
// SKIP: ($PIXELFOGTYPE == 0) && ($WRITEWATERFOGTODESTALPHA != 0)
// We don't care about flashlight depth unless the flashlight is on
// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 )
// Flashlight shadow filter mode is irrelevant if there is no flashlight
// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 )
#include "common_ps_fxc.h"
#include "common_flashlight_fxc.h"
#include "shader_constant_register_map.h"
// Universal Constants
static const float PI = 3.141592;
static const float ONE_OVER_PI = 0.318309;
static const float EPSILON = 0.00001;
// Shlick's approximation of the Fresnel factor
float3 fresnelSchlick(float3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
// Shlick's approximation of the Fresnel factor with account for roughness
float3 fresnelSchlickRoughness(float3 F0, float cosTheta, float roughness)
{
return F0 + (max(float3(1.0, 1.0, 1.0) - roughness, F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// GGX/Towbridge-Reitz normal distribution function
// Uses Disney's reparametrization of alpha = roughness^2
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method
float gaSchlickGGX(float cosLi, float cosLo, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
// Monte Carlo integration, approximate analytic version based on Dimitar Lazarov's work
// https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
float3 EnvBRDFApprox(float3 SpecularColor, float Roughness, float NoV)
{
const float4 c0 = { -1, -0.0275, -0.572, 0.022 };
const float4 c1 = { 1, 0.0425, 1.04, -0.04 };
float4 r = Roughness * c0 + c1;
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
float2 AB = float2(-1.04, 1.04) * a004 + r.zw;
return SpecularColor * AB.x + AB.y;
}
// Compute the matrix used to transform tangent space normals to world space
// This expects DirectX normal maps in Mikk Tangent Space http://www.mikktspace.com
float3x3 compute_tangent_frame(float3 N, float3 P, float2 uv, out float3 T, out float3 B, out float sign_det)
{
float3 dp1 = ddx(P);
float3 dp2 = ddy(P);
float2 duv1 = ddx(uv);
float2 duv2 = ddy(uv);
sign_det = dot(dp2, cross(N, dp1)) > 0.0 ? -1 : 1;
float3x3 M = float3x3(dp1, dp2, cross(dp1, dp2));
float2x3 inverseM = float2x3(cross(M[1], M[2]), cross(M[2], M[0]));
T = normalize(mul(float2(duv1.x, duv2.x), inverseM));
B = normalize(mul(float2(duv1.y, duv2.y), inverseM));
return float3x3(T, B, N);
}
float GetAttenForLight(float4 lightAtten, int lightNum)
{
#if NUM_LIGHTS > 1
if (lightNum == 1)
return lightAtten.y;
#endif
#if NUM_LIGHTS > 2
if (lightNum == 2)
return lightAtten.z;
#endif
#if NUM_LIGHTS > 3
if (lightNum == 3)
return lightAtten.w;
#endif
return lightAtten.x;
}
// Calculate direct light for one source
float3 calculateLight(float3 lightIn, float3 lightIntensity, float3 lightOut, float3 normal, float3 fresnelReflectance, float roughness, float metalness, float lightDirectionAngle, float3 albedo)
{
// Lh
float3 HalfAngle = normalize(lightIn + lightOut);
float cosLightIn = max(0.0, dot(normal, lightIn));
float cosHalfAngle = max(0.0, dot(normal, HalfAngle));
// F - Calculate Fresnel term for direct lighting
float3 F = fresnelSchlick(fresnelReflectance, max(0.0, dot(HalfAngle, lightOut)));
// D - Calculate normal distribution for specular BRDF
float D = ndfGGX(cosHalfAngle, roughness);
// Calculate geometric attenuation for specular BRDF
float G = gaSchlickGGX(cosLightIn, lightDirectionAngle, roughness);
// Diffuse scattering happens due to light being refracted multiple times by a dielectric medium
// Metals on the other hand either reflect or absorb energso diffuse contribution is always, zero
// To be energy conserving we must scale diffuse BRDF contribution based on Fresnel factor & metalness
#if SPECULAR
// Metalness is not used if F0 map is available
float3 kd = float3(1, 1, 1) - F;
#else
float3 kd = lerp(float3(1, 1, 1) - F, float3(0, 0, 0), metalness);
#endif
float3 diffuseBRDF = kd * albedo;
// Cook-Torrance specular microfacet BRDF
float3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLightIn * lightDirectionAngle);
#if LIGHTMAPPED && !FLASHLIGHT
// Ambient light from static lights is already precomputed in the lightmap. Don't add it again
return specularBRDF * lightIntensity * cosLightIn;
#else
return (diffuseBRDF + specularBRDF) * lightIntensity * cosLightIn;
#endif
}
// Get diffuse ambient light
float3 ambientLookupLightmap(float3 normal, float3 EnvAmbientCube[6], float3 textureNormal, float4 lightmapTexCoord1And2, float4 lightmapTexCoord3, sampler LightmapSampler, float4 g_DiffuseModulation)
{
float2 bumpCoord1;
float2 bumpCoord2;
float2 bumpCoord3;
ComputeBumpedLightmapCoordinates(lightmapTexCoord1And2, lightmapTexCoord3.xy, bumpCoord1, bumpCoord2, bumpCoord3);
float3 lightmapColor1 = tex2D(LightmapSampler, bumpCoord1).rgb;
float3 lightmapColor2 = tex2D(LightmapSampler, bumpCoord2).rgb;
float3 lightmapColor3 = tex2D(LightmapSampler, bumpCoord3).rgb;
float3 dp;
dp.x = saturate(dot(textureNormal, bumpBasis[0]));
dp.y = saturate(dot(textureNormal, bumpBasis[1]));
dp.z = saturate(dot(textureNormal, bumpBasis[2]));
dp *= dp;
float3 diffuseLighting = dp.x * lightmapColor1 + dp.y * lightmapColor2 + dp.z * lightmapColor3;
float sum = dot(dp, float3(1, 1, 1));
diffuseLighting *= g_DiffuseModulation.xyz / sum;
return diffuseLighting;
}
float3 ambientLookup(float3 normal, float3 EnvAmbientCube[6], float3 textureNormal, float4 lightmapTexCoord1And2, float4 lightmapTexCoord3, sampler LightmapSampler, float4 g_DiffuseModulation)
{
#if LIGHTMAPPED
return ambientLookupLightmap(normal, EnvAmbientCube, textureNormal, lightmapTexCoord1And2, lightmapTexCoord3, LightmapSampler, g_DiffuseModulation);
#else
return PixelShaderAmbientLight(normal, EnvAmbientCube);
#endif
}
// Create an ambient cube from the envmap
void setupEnvMapAmbientCube(out float3 EnvAmbientCube[6], sampler EnvmapSampler)
{
float4 directionPosX = { 1, 0, 0, 12 }; float4 directionNegX = {-1, 0, 0, 12 };
float4 directionPosY = { 0, 1, 0, 12 }; float4 directionNegY = { 0,-1, 0, 12 };
float4 directionPosZ = { 0, 0, 1, 12 }; float4 directionNegZ = { 0, 0,-1, 12 };
EnvAmbientCube[0] = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, directionPosX).rgb;
EnvAmbientCube[1] = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, directionNegX).rgb;
EnvAmbientCube[2] = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, directionPosY).rgb;
EnvAmbientCube[3] = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, directionNegY).rgb;
EnvAmbientCube[4] = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, directionPosZ).rgb;
EnvAmbientCube[5] = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, directionNegZ).rgb;
}
#if PARALLAXOCCLUSION
float2 parallaxCorrect(float2 texCoord, float3 viewRelativeDir, sampler depthMap, float parallaxDepth, float parallaxCenter)
{
float fLength = length( viewRelativeDir );
float fParallaxLength = sqrt( fLength * fLength - viewRelativeDir.z * viewRelativeDir.z ) / viewRelativeDir.z;
float2 vParallaxDirection = normalize( viewRelativeDir.xy );
float2 vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
vParallaxOffsetTS *= parallaxDepth;
// Compute all the derivatives:
float2 dx = ddx( texCoord );
float2 dy = ddy( texCoord );
int nNumSteps = 20;
float fCurrHeight = 0.0;
float fStepSize = 1.0 / (float) nNumSteps;
float fPrevHeight = 1.0;
float fNextHeight = 0.0;
int nStepIndex = 0;
bool bCondition = true;
float2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
float2 vTexCurrentOffset = texCoord;
float fCurrentBound = 1.0;
float fParallaxAmount = 0.0;
float2 pt1 = 0;
float2 pt2 = 0;
float2 texOffset2 = 0;
while ( nStepIndex < nNumSteps )
{
vTexCurrentOffset -= vTexOffsetPerStep;
// Sample height map which in this case is stored in the alpha channel of the normal map:
fCurrHeight = parallaxCenter + tex2Dgrad( depthMap, vTexCurrentOffset, dx, dy ).a;
fCurrentBound -= fStepSize;
if ( fCurrHeight > fCurrentBound )
{
pt1 = float2( fCurrentBound, fCurrHeight );
pt2 = float2( fCurrentBound + fStepSize, fPrevHeight );
texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;
nStepIndex = nNumSteps + 1;
}
else
{
nStepIndex++;
fPrevHeight = fCurrHeight;
}
} // End of while ( nStepIndex < nNumSteps )
float fDelta2 = pt2.x - pt2.y;
float fDelta1 = pt1.x - pt1.y;
fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / ( fDelta2 - fDelta1 );
float2 vParallaxOffset = vParallaxOffsetTS * (1 - fParallaxAmount);
// The computed texture offset for the displaced point on the pseudo-extruded surface:
float2 texSample = texCoord - vParallaxOffset;
return texSample;
}
#endif
float3 worldToRelative(float3 worldVector, float3 surfTangent, float3 surfBasis, float3 surfNormal)
{
return float3(dot(worldVector, surfTangent), dot(worldVector, surfBasis), dot(worldVector, surfNormal));
}
const float4 g_DiffuseModulation : register(PSREG_DIFFUSE_MODULATION);
const float4 g_ShadowTweaks : register(PSREG_ENVMAP_TINT__SHADOW_TWEAKS);
const float3 cAmbientCube[6] : register(PSREG_AMBIENT_CUBE);
const float4 g_EyePos : register(PSREG_EYEPOS_SPEC_EXPONENT);
const float4 g_FogParams : register(PSREG_FOG_PARAMS);
const float4 g_FlashlightAttenuationFactors : register(PSREG_FLASHLIGHT_ATTENUATION);
const float4 g_FlashlightPos : register(PSREG_FLASHLIGHT_POSITION_RIM_BOOST);
const float4x4 g_FlashlightWorldToTexture : register(PSREG_FLASHLIGHT_TO_WORLD_TEXTURE);
PixelShaderLightInfo cLightInfo[3] : register(PSREG_LIGHT_INFO_ARRAY); // 2 registers each - 6 registers total (4th light spread across w's)
const float4 g_BaseColor : register(PSREG_SELFILLUMTINT);
#if PARALLAXOCCLUSION
const float4 g_ParallaxParms : register(PSREG_FREE); // c40? That breaks in SP branch. Too bad!
#define PARALLAX_DEPTH g_ParallaxParms.r
#define PARALLAX_CENTER g_ParallaxParms.g
#endif
sampler BaseTextureSampler : register(s0); // Base map, selfillum in alpha
sampler NormalTextureSampler : register(s1); // Normal map
sampler EnvmapSampler : register(s2); // Cubemap
sampler ShadowDepthSampler : register(s4); // Flashlight shadow depth map sampler
sampler RandRotSampler : register(s5); // RandomRotation sampler
sampler FlashlightSampler : register(s6); // Flashlight cookie
sampler LightmapSampler : register(s7); // Lightmap
sampler MRAOTextureSampler : register(s10); // MRAO texture
#if EMISSIVE
sampler EmissionTextureSampler : register(s11); // Emission texture
#endif
#if SPECULAR
sampler SpecularTextureSampler : register(s12); // Specular F0 texture
#endif
#define ENVMAPLOD (g_EyePos.a)
struct PSInput {
float2 baseTexCoord : TEXCOORD0;
float4 lightAtten : TEXCOORD1;
float3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
float3 projPos : TEXCOORD4;
float4 lightmapTexCoord1And2 : TEXCOORD5;
float4 lightmapTexCoord3 : TEXCOORD6;
};
// Entry point
float4 main(PSInput i) : COLOR
{
#if USEENVAMBIENT
float3 EnvAmbientCube[6];
setupEnvMapAmbientCube(EnvAmbientCube, EnvmapSampler);
#else
#define EnvAmbientCube cAmbientCube
#endif
float3 surfNormal = normalize(i.worldNormal);
float3 surfTangent;
float3 surfBase;
float flipSign;
float3x3 normalBasis = compute_tangent_frame(surfNormal, i.worldPos, i.baseTexCoord , surfTangent, surfBase, flipSign);
#if PARALLAXOCCLUSION
float3 outgoingLightRay = g_EyePos.xyz - i.worldPos;
float3 outgoingLightDirectionTS = worldToRelative( outgoingLightRay, surfTangent, surfBase, surfNormal);
float2 correctedTexCoord = parallaxCorrect(i.baseTexCoord, outgoingLightDirectionTS , NormalTextureSampler , PARALLAX_DEPTH , PARALLAX_CENTER);
#else
float2 correctedTexCoord = i.baseTexCoord;
#endif
float3 textureNormal = normalize((tex2D( NormalTextureSampler, correctedTexCoord).xyz - float3(0.5, 0.5, 0.5)) * 2);
float3 normal = normalize(mul(textureNormal, normalBasis)); // World Normal
float4 albedo = tex2D(BaseTextureSampler, correctedTexCoord);
albedo.xyz *= g_BaseColor.xyz;
float3 mrao = tex2D(MRAOTextureSampler, correctedTexCoord).xyz;
float metalness = mrao.x, roughness = mrao.y, ambientOcclusion = mrao.z;
#if EMISSIVE
float3 emission = tex2D(EmissionTextureSampler, correctedTexCoord).xyz;
#endif
#if SPECULAR
float3 specular = tex2D(SpecularTextureSampler, correctedTexCoord).xyz;
#endif
textureNormal.y *= flipSign; // Fixup textureNormal for ambient lighting
float3 outgoingLightDirection = normalize(g_EyePos.xyz - i.worldPos); // Lo
float lightDirectionAngle = max(0, dot(normal, outgoingLightDirection)); // cosLo
float3 specularReflectionVector = 2.0 * lightDirectionAngle * normal - outgoingLightDirection; // Lr
#if SPECULAR
float3 fresnelReflectance = specular.rgb; // F0
#else
float3 dielectricCoefficient = 0.04; //F0 dielectric
float3 fresnelReflectance = lerp(dielectricCoefficient, albedo.rgb, metalness); // F0
#endif
// Start ambient
float3 ambientLighting = 0.0;
if (!FLASHLIGHT)
{
float3 diffuseIrradiance = ambientLookup(normal, EnvAmbientCube, textureNormal, i.lightmapTexCoord1And2, i.lightmapTexCoord3, LightmapSampler, g_DiffuseModulation);
float3 ambientLightingFresnelTerm = fresnelSchlickRoughness(fresnelReflectance, lightDirectionAngle, roughness); // F
#if SPECULAR
float3 diffuseContributionFactor = 1 - ambientLightingFresnelTerm; // kd
#else
float3 diffuseContributionFactor = lerp(1 - ambientLightingFresnelTerm, 0, metalness); ; // kd
#endif
float3 diffuseIBL = diffuseContributionFactor * albedo.rgb * diffuseIrradiance;
float4 specularUV = float4(specularReflectionVector, roughness * ENVMAPLOD);
float3 lookupHigh = ENV_MAP_SCALE * texCUBElod(EnvmapSampler, specularUV).xyz;
float3 lookupLow = PixelShaderAmbientLight(specularReflectionVector, EnvAmbientCube);
float3 specularIrradiance = lerp(lookupHigh, lookupLow, roughness * roughness);
float3 specularIBL = specularIrradiance * EnvBRDFApprox(fresnelReflectance, roughness, lightDirectionAngle);
ambientLighting = (diffuseIBL + specularIBL) * ambientOcclusion;
}
// End ambient
// Start direct
float3 directLighting = 0.0;
if (!FLASHLIGHT) {
for (uint n = 0; n < NUM_LIGHTS; ++n) {
float3 LightIn = normalize(PixelShaderGetLightVector(i.worldPos, cLightInfo, n));
float3 LightColor = PixelShaderGetLightColor(cLightInfo, n) * GetAttenForLight(i.lightAtten, n); // Li
directLighting += calculateLight(LightIn, LightColor, outgoingLightDirection, normal, fresnelReflectance, roughness, metalness, lightDirectionAngle, albedo.rgb);
}
}
// End direct
// Start flashlight
if (FLASHLIGHT) {
float4 flashlightSpacePosition = mul(float4(i.worldPos, 1.0), g_FlashlightWorldToTexture);
clip( flashlightSpacePosition.w ); // stop projected textures from projecting backwards (only really happens if they have a big FOV because they get frustum culled.)
float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
float3 delta = g_FlashlightPos.xyz - i.worldPos;
float distSquared = dot(delta, delta);
float dist = sqrt(distSquared);
float3 flashlightColor = tex2D(FlashlightSampler, vProjCoords.xy).xyz;
flashlightColor *= cFlashlightColor.xyz;
#if FLASHLIGHTSHADOWS
float flashlightShadow = DoFlashlightShadow(ShadowDepthSampler, RandRotSampler, vProjCoords, i.projPos.xy, FLASHLIGHTDEPTHFILTERMODE, g_ShadowTweaks, true);
float flashlightAttenuated = lerp(flashlightShadow, 1.0, g_ShadowTweaks.y); // Blend between fully attenuated and not attenuated
float fAtten = saturate(dot(g_FlashlightAttenuationFactors.xyz, float3(1.0, 1.0 / dist, 1.0 / distSquared)));
flashlightShadow = saturate(lerp(flashlightAttenuated, flashlightShadow, fAtten)); // Blend between shadow and above, according to light attenuation
flashlightColor *= flashlightShadow;
#endif
float farZ = g_FlashlightAttenuationFactors.w;
float endFalloffFactor = RemapValClamped(dist, farZ, 0.6 * farZ, 0.0, 1.0);
float3 flashLightIntensity = flashlightColor * endFalloffFactor;
float3 flashLightIn = normalize(g_FlashlightPos.xyz - i.worldPos);
directLighting += max(0, calculateLight(flashLightIn, flashLightIntensity, outgoingLightDirection, normal, fresnelReflectance, roughness, metalness, lightDirectionAngle, albedo.rgb));
}
// End flashlight
float fogFactor = 0.0f;
#if !FLASHLIGHT
fogFactor = CalcPixelFogFactor(PIXELFOGTYPE, g_FogParams, g_EyePos.z, i.worldPos.z, i.projPos.z);
#endif
float alpha = 0.0f;
#if !FLASHLIGHT
#if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT)
alpha = fogFactor;
#else
alpha = albedo.a;
#endif
#endif
bool bWriteDepthToAlpha = (WRITE_DEPTH_TO_DESTALPHA != 0) && (WRITEWATERFOGTODESTALPHA == 0);
float3 combinedLighting = directLighting + ambientLighting;
#if EMISSIVE && !FLASHLIGHT
combinedLighting += emission;
#endif
return FinalOutput(float4(combinedLighting, alpha), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, bWriteDepthToAlpha, i.projPos.z);
}
// DYNAMIC: "COMPRESSED_VERTS" "0..1"
// DYNAMIC: "DOWATERFOG" "0..1"
// DYNAMIC: "SKINNING" "0..1"
// DYNAMIC: "LIGHTING_PREVIEW" "0..1"
// DYNAMIC: "NUM_LIGHTS" "0..4"
#include "common_vs_fxc.h"
static const bool g_bSkinning = SKINNING;
static const int g_FogType = DOWATERFOG;
const float4 cBaseTexCoordTransform[2] : register(SHADER_SPECIFIC_CONST_0);
struct VSInput {
float4 vPos : POSITION;
float4 vBoneWeights : BLENDWEIGHT;
float4 vBoneIndices : BLENDINDICES;
float4 vNormal : NORMAL;
float2 vTexCoord0 : TEXCOORD0;
float4 vLightmapTexCoord : TEXCOORD1;
float4 vLightmapTexCoordOffset : TEXCOORD2;
};
struct VSOutput {
float4 projPosSetup : POSITION;
float fog : FOG;
float2 baseTexCoord : TEXCOORD0;
float4 lightAtten : TEXCOORD1;
float3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
float3 projPos : TEXCOORD4;
float4 lightmapTexCoord1And2 : TEXCOORD5;
float4 lightmapTexCoord3 : TEXCOORD6;
};
VSOutput main(const VSInput input)
{
VSOutput output = (VSOutput)0;
output.lightmapTexCoord3.z = dot(input.vTexCoord0, cBaseTexCoordTransform[0].xy) + cBaseTexCoordTransform[0].w;
output.lightmapTexCoord3.w = dot(input.vTexCoord0, cBaseTexCoordTransform[1].xy) + cBaseTexCoordTransform[1].w;
output.lightmapTexCoord1And2.xy = input.vLightmapTexCoord.xy + input.vLightmapTexCoordOffset.xy;
float2 lightmapTexCoord2 = output.lightmapTexCoord1And2.xy + input.vLightmapTexCoordOffset.xy;
float2 lightmapTexCoord3 = lightmapTexCoord2 + input.vLightmapTexCoordOffset.xy;
// Reversed component order
output.lightmapTexCoord1And2.w = lightmapTexCoord2.x;
output.lightmapTexCoord1And2.z = lightmapTexCoord2.y;
output.lightmapTexCoord3.xy = lightmapTexCoord3;
float3 vNormal;
DecompressVertex_Normal(input.vNormal, vNormal);
float3 worldNormal, worldPos;
SkinPositionAndNormal(g_bSkinning, input.vPos, vNormal, input.vBoneWeights, input.vBoneIndices, worldPos, worldNormal);
// Transform into projection space
float4 vProjPos = mul(float4(worldPos, 1), cViewProj);
output.projPosSetup = vProjPos;
vProjPos.z = dot(float4(worldPos, 1), cViewProjZ);
output.projPos = vProjPos.xyz;
output.fog = CalcFog(worldPos, vProjPos.xyz, g_FogType);
// Needed for water fog alpha and diffuse lighting
output.worldPos = worldPos;
output.worldNormal = normalize(worldNormal);
// Scalar attenuations for four lights
output.lightAtten = float4(0, 0, 0, 0);
#if NUM_LIGHTS > 0
output.lightAtten.x = GetVertexAttenForLight(worldPos, 0, false);
#endif
#if NUM_LIGHTS > 1
output.lightAtten.y = GetVertexAttenForLight(worldPos, 1, false);
#endif
#if NUM_LIGHTS > 2
output.lightAtten.z = GetVertexAttenForLight(worldPos, 2, false);
#endif
#if NUM_LIGHTS > 3
output.lightAtten.w = GetVertexAttenForLight(worldPos, 3, false);
#endif
// Base texture coordinate transform
output.baseTexCoord.x = dot(input.vTexCoord0, cBaseTexCoordTransform[0].xy);
output.baseTexCoord.y = dot(input.vTexCoord0, cBaseTexCoordTransform[1].xy);
output.baseTexCoord = input.vTexCoord0;
return output;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment