Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 211 additions & 0 deletions Game.Assets/hl2/shaders/common_flashlight_gl460.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#ifndef COMMON_FLASHLIGHT_GL460_FS
#define COMMON_FLASHLIGHT_GL460_FS

#include "common_gl460.fs"

float DoShadowPoisson16Sample(sampler2DShadow DepthSampler, sampler2D DepthSamplerRaw, sampler2D RandomRotationSampler, vec3 vProjCoords, vec2 vScreenPos, vec4 vShadowTweaks, bool bNvidiaHardwarePCF, bool bFetch4)
{
vec2 vPoissonOffset[8] = vec2[8]( vec2( 0.3475, 0.0042 ),
vec2( 0.8806, 0.3430 ),
vec2( -0.0041, -0.6197 ),
vec2( 0.0472, 0.4964 ),
vec2( -0.3730, 0.0874 ),
vec2( -0.9217, -0.3177 ),
vec2( -0.6289, 0.7388 ),
vec2( 0.5744, -0.7741 ) );

float flScaleOverMapSize = vShadowTweaks.x * 2.0; // Tweak parameters to shader
vec2 vNoiseOffset = vShadowTweaks.zw;
vec4 vLightDepths = vec4(0.0), accum = vec4(0.0);
vec2 rotOffset = vec2(0.0);

vec2 shadowMapCenter = vProjCoords.xy; // Center of shadow filter
float objDepth = min(vProjCoords.z, 0.99999); // Object depth in shadow space

// 2D Rotation Matrix setup
vec3 RMatTop = vec3(0.0), RMatBottom = vec3(0.0);
RMatTop.xy = texture(RandomRotationSampler, cFlashlightScreenScale.xy * (vScreenPos * 0.5 + 0.5) + vNoiseOffset).xy * 2.0 - 1.0;
RMatBottom.xy = vec2(-1.0, 1.0) * RMatTop.yx; // 2x2 rotation matrix in 4-tuple

RMatTop *= flScaleOverMapSize; // Scale up kernel while accounting for texture resolution
RMatBottom *= flScaleOverMapSize;

RMatTop.z = shadowMapCenter.x; // To be added in d2adds generated below
RMatBottom.z = shadowMapCenter.y;

float fResult = 0.0;

if (bNvidiaHardwarePCF)
{
for (int i = 0; i < 8; i++)
{
rotOffset.x = dot(RMatTop.xy, vPoissonOffset[i].xy) + RMatTop.z;
rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[i].xy) + RMatBottom.z;
vLightDepths[i & 3] += texture(DepthSampler, vec3(rotOffset, objDepth));
}

fResult = dot(vLightDepths, vec4(0.25, 0.25, 0.25, 0.25));
}
else if (bFetch4)
{
for (int i = 0; i < 8; i++)
{
rotOffset.x = dot(RMatTop.xy, vPoissonOffset[i].xy) + RMatTop.z;
rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[i].xy) + RMatBottom.z;
vLightDepths = texture(DepthSamplerRaw, rotOffset.xy);
accum += vec4(greaterThan(vLightDepths, vec4(objDepth)));
}

fResult = dot(accum, vec4(1.0 / 32.0, 1.0 / 32.0, 1.0 / 32.0, 1.0 / 32.0));
}
else // ATI vanilla hardware shadow mapping
{
for (int i = 0; i < 2; i++)
{
rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 0].xy) + RMatTop.z;
rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 0].xy) + RMatBottom.z;
vLightDepths.x = texture(DepthSamplerRaw, rotOffset.xy).x;

rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 1].xy) + RMatTop.z;
rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 1].xy) + RMatBottom.z;
vLightDepths.y = texture(DepthSamplerRaw, rotOffset.xy).x;

rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 2].xy) + RMatTop.z;
rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 2].xy) + RMatBottom.z;
vLightDepths.z = texture(DepthSamplerRaw, rotOffset.xy).x;

rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 3].xy) + RMatTop.z;
rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 3].xy) + RMatBottom.z;
vLightDepths.w = texture(DepthSamplerRaw, rotOffset.xy).x;

accum += vec4(greaterThan(vLightDepths, vec4(objDepth)));
}

fResult = dot(accum, vec4(0.125, 0.125, 0.125, 0.125));
}

return fResult;
}

float DoFlashlightShadow(sampler2DShadow DepthSampler, sampler2D DepthSamplerRaw, sampler2D RandomRotationSampler, vec3 vProjCoords, vec2 vScreenPos, int nShadowLevel, vec4 vShadowTweaks, bool bAllowHighQuality)
{
float flShadow = 1.0;

if (nShadowLevel == NVIDIA_PCF_POISSON)
flShadow = DoShadowPoisson16Sample(DepthSampler, DepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, true, false);
else if (nShadowLevel == ATI_NOPCF)
flShadow = DoShadowPoisson16Sample(DepthSampler, DepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, false);
else if (nShadowLevel == ATI_NO_PCF_FETCH4)
flShadow = DoShadowPoisson16Sample(DepthSampler, DepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, true);

return flShadow;
}

vec3 SpecularLight(vec3 vWorldNormal, vec3 vLightDir, float fSpecularExponent,
vec3 vEyeDir, bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel)
{
vec3 result = vec3(0.0, 0.0, 0.0);

vec3 vReflect = 2.0 * vWorldNormal * dot(vWorldNormal, vEyeDir) - vEyeDir; // Reflect view through normal
vec3 vSpecular = vec3(clamp(dot(vReflect, vLightDir), 0.0, 1.0)); // L.R (use half-angle instead?)
vSpecular = vec3(pow(vSpecular.x, fSpecularExponent)); // Raise to specular power

// Optionally warp as function of scalar specular and fresnel
if (bDoSpecularWarp)
vSpecular *= texture(specularWarpSampler, vec2(vSpecular.x, fFresnel)).xyz; // Sample at { (L.R)^k, fresnel }

return vSpecular;
}

void DoSpecularFlashlight(vec3 flashlightPos, vec3 worldPos, vec4 flashlightSpacePosition, vec3 worldNormal,
vec3 attenuationFactors, float farZ, sampler2D FlashlightSampler, sampler2DShadow FlashlightDepthSampler, sampler2D FlashlightDepthSamplerRaw, sampler2D RandomRotationSampler,
int nShadowLevel, bool bDoShadows, bool bAllowHighQuality, vec2 vScreenPos, float fSpecularExponent, vec3 vEyeDir,
bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel, vec4 vShadowTweaks,

// Outputs of this shader...separate shadowed diffuse and specular from the flashlight
out vec3 diffuseLighting, out vec3 specularLighting)
{
vec3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
vec3 flashlightColor = texture(FlashlightSampler, vProjCoords.xy).xyz;

flashlightColor *= cFlashlightColor.xyz; // Flashlight color

vec3 delta = flashlightPos - worldPos;
vec3 L = normalize(delta);
float distSquared = dot(delta, delta);
float dist = sqrt(distSquared);

float endFalloffFactor = RemapValClamped(dist, farZ, 0.6 * farZ, 0.0, 1.0);

// Attenuation for light and to fade out shadow over distance
float fAtten = clamp(dot(attenuationFactors, vec3(1.0, 1.0 / dist, 1.0 / distSquared)), 0.0, 1.0);

// Shadowing and coloring terms
if (bDoShadows)
{
float flShadow = DoFlashlightShadow(FlashlightDepthSampler, FlashlightDepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality);
float flAttenuated = mix(flShadow, 1.0, vShadowTweaks.y); // Blend between fully attenuated and not attenuated
flShadow = clamp(mix(flAttenuated, flShadow, fAtten), 0.0, 1.0); // Blend between shadow and above, according to light attenuation
flashlightColor *= flShadow; // Shadow term
}

diffuseLighting = vec3(fAtten);
diffuseLighting *= clamp(dot(L.xyz, worldNormal.xyz) + flFlashlightNoLambertValue, 0.0, 1.0); // Lambertian term
diffuseLighting *= flashlightColor;
diffuseLighting *= endFalloffFactor;

// Specular term (masked by diffuse)
specularLighting = diffuseLighting * SpecularLight(worldNormal, L, fSpecularExponent, vEyeDir, bDoSpecularWarp, specularWarpSampler, fFresnel);
}

// Diffuse only version
vec3 DoFlashlight(vec3 flashlightPos, vec3 worldPos, vec4 flashlightSpacePosition, vec3 worldNormal,
vec3 attenuationFactors, float farZ, sampler2D FlashlightSampler, sampler2DShadow FlashlightDepthSampler, sampler2D FlashlightDepthSamplerRaw,
sampler2D RandomRotationSampler, int nShadowLevel, bool bDoShadows, bool bAllowHighQuality,
vec2 vScreenPos, bool bClip, vec4 vShadowTweaks, bool bHasNormal)
{
vec3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
vec3 flashlightColor = texture(FlashlightSampler, vProjCoords.xy).xyz;

flashlightColor *= cFlashlightColor.xyz; // Flashlight color

vec3 delta = flashlightPos - worldPos;
vec3 L = normalize(delta);
float distSquared = dot(delta, delta);
float dist = sqrt(distSquared);

float endFalloffFactor = RemapValClamped(dist, farZ, 0.6 * farZ, 0.0, 1.0);

// Attenuation for light and to fade out shadow over distance
float fAtten = clamp(dot(attenuationFactors, vec3(1.0, 1.0 / dist, 1.0 / distSquared)), 0.0, 1.0);

// Shadowing and coloring terms
if (bDoShadows)
{
float flShadow = DoFlashlightShadow(FlashlightDepthSampler, FlashlightDepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality);
float flAttenuated = mix(flShadow, 1.0, vShadowTweaks.y); // Blend between fully attenuated and not attenuated
flShadow = clamp(mix(flAttenuated, flShadow, fAtten), 0.0, 1.0); // Blend between shadow and above, according to light attenuation
flashlightColor *= flShadow; // Shadow term
}

vec3 diffuseLighting = vec3(fAtten);

float flLDotWorldNormal;
if (bHasNormal)
{
flLDotWorldNormal = dot(L.xyz, worldNormal.xyz);
}
else
{
flLDotWorldNormal = 1.0;
}

diffuseLighting *= clamp(flLDotWorldNormal + flFlashlightNoLambertValue, 0.0, 1.0); // Lambertian term

diffuseLighting *= flashlightColor;
diffuseLighting *= endFalloffFactor;

return diffuseLighting;
}

#endif // COMMON_FLASHLIGHT_GL460_FS
163 changes: 163 additions & 0 deletions Game.Assets/hl2/shaders/common_gl460.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,40 @@

#include "common_gl460.glsl"

// System defined pixel shader constants

// NOTE: w == 1.0f / (Dest alpha compressed depth range).
#define g_LinearFogColor ps_const[29]
#define OO_DESTALPHA_DEPTH_RANGE (g_LinearFogColor.w)

// Linear and gamma light scale values
#define cLightScale ps_const[30]
#define LINEAR_LIGHT_SCALE (cLightScale.x)
#define LIGHT_MAP_SCALE (cLightScale.y)
#define ENV_MAP_SCALE (cLightScale.z)
#define GAMMA_LIGHT_SCALE (cLightScale.w)

// Flashlight constants
#define cFlashlightColor ps_const[28]
#define cFlashlightScreenScale ps_const[31] // .zw are currently unused
#define flFlashlightNoLambertValue cFlashlightColor.w // This is either 0.0 or 2.0

#define HDR_INPUT_MAP_SCALE 16.0

#define TONEMAP_SCALE_NONE 0
#define TONEMAP_SCALE_LINEAR 1
#define TONEMAP_SCALE_GAMMA 2

#define PIXEL_FOG_TYPE_NONE -1 //MATERIAL_FOG_NONE is handled by PIXEL_FOG_TYPE_RANGE, this is for explicitly disabling fog in the shader
#define PIXEL_FOG_TYPE_RANGE 0 //range+none packed together in ps2b. Simply none in ps20 (instruction limits)
#define PIXEL_FOG_TYPE_HEIGHT 1
#define PIXEL_FOG_TYPE_RANGE_RADIAL 2

// If you change these, make the corresponding change in hardwareconfig.cpp
#define NVIDIA_PCF_POISSON 0
#define ATI_NOPCF 1
#define ATI_NO_PCF_FETCH4 2

// texture combining modes for combining base and detail/basetexture2
#define TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2 0 // original mode
#define TCOMBINE_RGB_ADDITIVE 1 // base.rgb+detail.rgb*fblend
Expand Down Expand Up @@ -71,4 +105,133 @@ vec3 TextureCombinePostLighting(vec3 lit_baseColor, vec4 detailColor, int combin
return lit_baseColor;
}

float CalcWaterFogAlpha(float flWaterZ, float flEyePosZ, float flWorldPosZ, float flProjPosZ, float flFogOORange)
{
float flDepthFromWater = flWaterZ - flWorldPosZ;

// Calculate the ratio of water fog to regular fog (ie. how much of the distance from the viewer
// to the vert is actually underwater.
float flDepthFromEye = flEyePosZ - flWorldPosZ;
float f = clamp(flDepthFromWater * (1.0 / flDepthFromEye), 0.0, 1.0);

// $tmp.w is now the distance that we see through water.
return clamp(f * flProjPosZ * flFogOORange, 0.0, 1.0);
}

float CalcRangeFog(float flProjPosZ, float flFogStartOverRange, float flFogMaxDensity, float flFogOORange)
{
return clamp(min(flFogMaxDensity, (flProjPosZ * flFogOORange) - flFogStartOverRange), 0.0, 1.0);
}

float CalcPixelFogFactor(int iPIXELFOGTYPE, vec4 fogParams, float flEyePosZ, float flWorldPosZ, float flProjPosZ)
{
float retVal = 0.0;
if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE)
{
retVal = 0.0;
}
else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE) //range fog, or no fog depending on fog parameters
{
retVal = CalcRangeFog(flProjPosZ, fogParams.x, fogParams.z, fogParams.w);
}
else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) //height fog
{
retVal = CalcWaterFogAlpha(fogParams.y, flEyePosZ, flWorldPosZ, flProjPosZ, fogParams.w);
}

return retVal;
}

//g_FogParams not defined by default, but this is the same layout for every shader that does define it
#define g_FogEndOverRange g_FogParams.x
#define g_WaterZ g_FogParams.y
#define g_FogMaxDensity g_FogParams.z
#define g_FogOORange g_FogParams.w

vec3 BlendPixelFog(vec3 vShaderColor, float pixelFogFactor, vec3 vFogColor, int iPIXELFOGTYPE)
{
if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE || iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE_RADIAL) //either range fog or no fog depending on fog parameters and whether this is ps20 or ps2b
{
pixelFogFactor = clamp(pixelFogFactor, 0.0, 1.0);
return mix(vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor); //squaring the factor will get the middle range mixing closer to hardware fog
}
else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT)
{
return mix(vShaderColor.rgb, vFogColor.rgb, clamp(pixelFogFactor, 0.0, 1.0));
}
return vShaderColor;
}

// The framebuffer performs the linear->gamma conversion for us (GL_FRAMEBUFFER_SRGB), which is
// the equivalent of the CONVERT_TO_SRGB == 0 path.
vec3 SRGBOutput(vec3 vShaderColor)
{
return vShaderColor;
}

float SoftParticleDepth(float flDepth)
{
return flDepth * OO_DESTALPHA_DEPTH_RANGE;
}

float DepthToDestAlpha(float flProjZ)
{
return SoftParticleDepth(flProjZ);
}

vec4 FinalOutput(vec4 vShaderColor, float pixelFogFactor, int iPIXELFOGTYPE, int iTONEMAP_SCALE_TYPE, bool bWriteDepthToDestAlpha, float flProjZ)
{
vec4 result;
if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR)
{
result.rgb = vShaderColor.rgb * LINEAR_LIGHT_SCALE;
}
else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA)
{
result.rgb = vShaderColor.rgb * GAMMA_LIGHT_SCALE;
}
else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_NONE)
{
result.rgb = vShaderColor.rgb;
}

if (bWriteDepthToDestAlpha)
result.a = DepthToDestAlpha(flProjZ);
else
result.a = vShaderColor.a;

// TODO: fog
// result.rgb = BlendPixelFog(result.rgb, pixelFogFactor, g_LinearFogColor.rgb, iPIXELFOGTYPE);

result.rgb = SRGBOutput(result.rgb); //SRGB in pixel shader conversion

return result;
}

vec4 FinalOutput(vec4 vShaderColor, float pixelFogFactor, int iPIXELFOGTYPE, int iTONEMAP_SCALE_TYPE)
{
return FinalOutput(vShaderColor, pixelFogFactor, iPIXELFOGTYPE, iTONEMAP_SCALE_TYPE, false, 1.0);
}

float RemapValClamped(float val, float A, float B, float C, float D)
{
float cVal = (val - A) / (B - A);
cVal = clamp(cVal, 0.0, 1.0);

return C + (D - C) * cVal;
}

float DepthFeathering(sampler2D DepthSampler, vec2 vScreenPos, float fProjZ, float fProjW, vec4 vDepthBlendConstants)
{
float flFeatheredAlpha;
float flSceneDepth = texture(DepthSampler, vScreenPos).a; // PC uses dest alpha of the frame buffer
float flSpriteDepth = SoftParticleDepth(fProjZ);

flFeatheredAlpha = abs(flSceneDepth - flSpriteDepth) * vDepthBlendConstants.x;
flFeatheredAlpha = max(smoothstep(0.75, 1.0, flSceneDepth), flFeatheredAlpha); //as the sprite approaches the edge of our compressed depth space, the math stops working. So as the sprite approaches the far depth, smoothly remove feathering.
flFeatheredAlpha = clamp(flFeatheredAlpha, 0.0, 1.0);

return flFeatheredAlpha;
}

#endif // COMMON_GL460_FS
Loading