mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-12-11 05:01:01 +00:00
474 lines
12 KiB
GLSL
474 lines
12 KiB
GLSL
uniform sampler2D u_DiffuseMap;
|
|
|
|
#if defined(USE_LIGHTMAP)
|
|
uniform sampler2D u_LightMap;
|
|
#endif
|
|
|
|
#if defined(USE_NORMALMAP)
|
|
uniform sampler2D u_NormalMap;
|
|
#endif
|
|
|
|
#if defined(USE_DELUXEMAP)
|
|
uniform sampler2D u_DeluxeMap;
|
|
#endif
|
|
|
|
#if defined(USE_SPECULARMAP)
|
|
uniform sampler2D u_SpecularMap;
|
|
#endif
|
|
|
|
#if defined(USE_SHADOWMAP)
|
|
uniform sampler2D u_ShadowMap;
|
|
#endif
|
|
|
|
#if defined(USE_CUBEMAP)
|
|
uniform samplerCube u_CubeMap;
|
|
#endif
|
|
|
|
#if defined(USE_NORMALMAP) || defined(USE_DELUXEMAP) || defined(USE_SPECULARMAP) || defined(USE_CUBEMAP)
|
|
// y = deluxe, w = cube
|
|
uniform vec4 u_EnableTextures;
|
|
#endif
|
|
|
|
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
|
|
uniform vec3 u_PrimaryLightColor;
|
|
uniform vec3 u_PrimaryLightAmbient;
|
|
#endif
|
|
|
|
#if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
|
|
uniform vec4 u_NormalScale;
|
|
uniform vec4 u_SpecularScale;
|
|
#endif
|
|
|
|
#if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
|
|
#if defined(USE_CUBEMAP)
|
|
uniform vec4 u_CubeMapInfo;
|
|
#endif
|
|
#endif
|
|
|
|
uniform int u_AlphaTest;
|
|
|
|
varying vec4 var_TexCoords;
|
|
|
|
varying vec4 var_Color;
|
|
#if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT))
|
|
varying vec4 var_ColorAmbient;
|
|
#endif
|
|
|
|
#if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT))
|
|
varying vec4 var_Normal;
|
|
varying vec4 var_Tangent;
|
|
varying vec4 var_Bitangent;
|
|
#endif
|
|
|
|
#if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
|
|
varying vec4 var_LightDir;
|
|
#endif
|
|
|
|
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
|
|
varying vec4 var_PrimaryLightDir;
|
|
#endif
|
|
|
|
|
|
#define EPSILON 0.00000001
|
|
|
|
#if defined(USE_PARALLAXMAP)
|
|
float SampleDepth(sampler2D normalMap, vec2 t)
|
|
{
|
|
#if defined(SWIZZLE_NORMALMAP)
|
|
return 1.0 - texture2D(normalMap, t).r;
|
|
#else
|
|
return 1.0 - texture2D(normalMap, t).a;
|
|
#endif
|
|
}
|
|
|
|
float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap)
|
|
{
|
|
const int linearSearchSteps = 16;
|
|
const int binarySearchSteps = 6;
|
|
|
|
// current size of search window
|
|
float size = 1.0 / float(linearSearchSteps);
|
|
|
|
// current depth position
|
|
float depth = 0.0;
|
|
|
|
// best match found (starts with last position 1.0)
|
|
float bestDepth = 1.0;
|
|
|
|
// texture depth at best depth
|
|
float texDepth = 0.0;
|
|
|
|
float prevT = SampleDepth(normalMap, dp);
|
|
float prevTexDepth = prevT;
|
|
|
|
// search front to back for first point inside object
|
|
for(int i = 0; i < linearSearchSteps - 1; ++i)
|
|
{
|
|
depth += size;
|
|
|
|
float t = SampleDepth(normalMap, dp + ds * depth);
|
|
|
|
if(bestDepth > 0.996) // if no depth found yet
|
|
if(depth >= t)
|
|
{
|
|
bestDepth = depth; // store best depth
|
|
texDepth = t;
|
|
prevTexDepth = prevT;
|
|
}
|
|
prevT = t;
|
|
}
|
|
|
|
depth = bestDepth;
|
|
|
|
#if !defined (USE_RELIEFMAP)
|
|
float div = 1.0 / (1.0 + (prevTexDepth - texDepth) * float(linearSearchSteps));
|
|
bestDepth -= (depth - size - prevTexDepth) * div;
|
|
#else
|
|
// recurse around first point (depth) for closest match
|
|
for(int i = 0; i < binarySearchSteps; ++i)
|
|
{
|
|
size *= 0.5;
|
|
|
|
float t = SampleDepth(normalMap, dp + ds * depth);
|
|
|
|
if(depth >= t)
|
|
{
|
|
bestDepth = depth;
|
|
depth -= 2.0 * size;
|
|
}
|
|
|
|
depth += size;
|
|
}
|
|
#endif
|
|
|
|
return bestDepth;
|
|
}
|
|
#endif
|
|
|
|
vec3 CalcDiffuse(vec3 diffuseAlbedo, float NH, float EH, float roughness)
|
|
{
|
|
#if defined(USE_BURLEY)
|
|
// modified from https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf
|
|
float fd90 = -0.5 + EH * EH * roughness;
|
|
float burley = 1.0 + fd90 * 0.04 / NH;
|
|
burley *= burley;
|
|
return diffuseAlbedo * burley;
|
|
#else
|
|
return diffuseAlbedo;
|
|
#endif
|
|
}
|
|
|
|
vec3 EnvironmentBRDF(float roughness, float NE, vec3 specular)
|
|
{
|
|
// from http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf
|
|
float v = 1.0 - max(roughness, NE);
|
|
v *= v * v;
|
|
return vec3(v) + specular;
|
|
}
|
|
|
|
vec3 CalcSpecular(vec3 specular, float NH, float EH, float roughness)
|
|
{
|
|
// from http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf
|
|
float rr = roughness*roughness;
|
|
float rrrr = rr*rr;
|
|
float d = (NH * NH) * (rrrr - 1.0) + 1.0;
|
|
float v = (EH * EH) * (roughness + 0.5);
|
|
return specular * (rrrr / (4.0 * d * d * v));
|
|
}
|
|
|
|
|
|
float CalcLightAttenuation(float point, float normDist)
|
|
{
|
|
// zero light at 1.0, approximating q3 style
|
|
// also don't attenuate directional light
|
|
float attenuation = (0.5 * normDist - 1.5) * point + 1.0;
|
|
|
|
// clamp attenuation
|
|
#if defined(NO_LIGHT_CLAMP)
|
|
attenuation = max(attenuation, 0.0);
|
|
#else
|
|
attenuation = clamp(attenuation, 0.0, 1.0);
|
|
#endif
|
|
|
|
return attenuation;
|
|
}
|
|
|
|
|
|
vec4 hitCube(vec3 ray, vec3 pos, vec3 invSize, float lod, samplerCube tex)
|
|
{
|
|
// find any hits on cubemap faces facing the camera
|
|
vec3 scale = (sign(ray) - pos) / ray;
|
|
|
|
// find the nearest hit
|
|
float minScale = min(min(scale.x, scale.y), scale.z);
|
|
|
|
// if the nearest hit is behind the camera, ignore
|
|
// should not be necessary as long as pos is inside the cube
|
|
//if (minScale < 0.0)
|
|
//return vec4(0.0);
|
|
|
|
// calculate the hit position, that's our texture coordinates
|
|
vec3 tc = pos + ray * minScale;
|
|
|
|
// if the texture coordinates are outside the cube, ignore
|
|
// necessary since we're not fading out outside the cube
|
|
if (any(greaterThan(abs(tc), vec3(1.00001))))
|
|
return vec4(0.0);
|
|
|
|
// fade out when approaching the cubemap edges
|
|
//vec3 fade3 = abs(pos);
|
|
//float fade = max(max(fade3.x, fade3.y), fade3.z);
|
|
//fade = clamp(1.0 - fade, 0.0, 1.0);
|
|
|
|
//return vec4(textureCubeLod(tex, tc, lod).rgb * fade, fade);
|
|
return vec4(textureCubeLod(tex, tc, lod).rgb, 1.0);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec3 viewDir, lightColor, ambientColor, reflectance;
|
|
vec3 L, N, E, H;
|
|
float NL, NH, NE, EH, attenuation;
|
|
|
|
#if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
|
|
mat3 tangentToWorld = mat3(var_Tangent.xyz, var_Bitangent.xyz, var_Normal.xyz);
|
|
viewDir = vec3(var_Normal.w, var_Tangent.w, var_Bitangent.w);
|
|
E = normalize(viewDir);
|
|
#endif
|
|
|
|
lightColor = var_Color.rgb;
|
|
|
|
#if defined(USE_LIGHTMAP)
|
|
vec4 lightmapColor = texture2D(u_LightMap, var_TexCoords.zw);
|
|
#if defined(RGBM_LIGHTMAP)
|
|
lightmapColor.rgb *= lightmapColor.a;
|
|
#endif
|
|
#if defined(USE_PBR) && !defined(USE_FAST_LIGHT)
|
|
lightmapColor.rgb *= lightmapColor.rgb;
|
|
#endif
|
|
lightColor *= lightmapColor.rgb;
|
|
#endif
|
|
|
|
vec2 texCoords = var_TexCoords.xy;
|
|
|
|
#if defined(USE_PARALLAXMAP)
|
|
vec3 offsetDir = viewDir * tangentToWorld;
|
|
|
|
offsetDir.xy *= -u_NormalScale.a / offsetDir.z;
|
|
|
|
texCoords += offsetDir.xy * RayIntersectDisplaceMap(texCoords, offsetDir.xy, u_NormalMap);
|
|
#endif
|
|
|
|
vec4 diffuse = texture2D(u_DiffuseMap, texCoords);
|
|
|
|
float alpha = diffuse.a * var_Color.a;
|
|
if (u_AlphaTest == 1)
|
|
{
|
|
if (alpha == 0.0)
|
|
discard;
|
|
}
|
|
else if (u_AlphaTest == 2)
|
|
{
|
|
if (alpha >= 0.5)
|
|
discard;
|
|
}
|
|
else if (u_AlphaTest == 3)
|
|
{
|
|
if (alpha < 0.5)
|
|
discard;
|
|
}
|
|
|
|
#if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
|
|
L = var_LightDir.xyz;
|
|
#if defined(USE_DELUXEMAP)
|
|
L += (texture2D(u_DeluxeMap, var_TexCoords.zw).xyz - vec3(0.5)) * u_EnableTextures.y;
|
|
#endif
|
|
float sqrLightDist = dot(L, L);
|
|
L /= sqrt(sqrLightDist);
|
|
|
|
#if defined(USE_LIGHT_VECTOR)
|
|
attenuation = CalcLightAttenuation(float(var_LightDir.w > 0.0), var_LightDir.w / sqrLightDist);
|
|
#else
|
|
attenuation = 1.0;
|
|
#endif
|
|
|
|
#if defined(USE_NORMALMAP)
|
|
#if defined(SWIZZLE_NORMALMAP)
|
|
N.xy = texture2D(u_NormalMap, texCoords).ag - vec2(0.5);
|
|
#else
|
|
N.xy = texture2D(u_NormalMap, texCoords).rg - vec2(0.5);
|
|
#endif
|
|
N.xy *= u_NormalScale.xy;
|
|
N.z = sqrt(clamp((0.25 - N.x * N.x) - N.y * N.y, 0.0, 1.0));
|
|
N = tangentToWorld * N;
|
|
#else
|
|
N = var_Normal.xyz;
|
|
#endif
|
|
|
|
N = normalize(N);
|
|
|
|
#if defined(USE_SHADOWMAP)
|
|
vec2 shadowTex = gl_FragCoord.xy * r_FBufScale;
|
|
float shadowValue = texture2D(u_ShadowMap, shadowTex).r;
|
|
|
|
// surfaces not facing the light are always shadowed
|
|
shadowValue *= clamp(dot(N, var_PrimaryLightDir.xyz), 0.0, 1.0);
|
|
|
|
#if defined(SHADOWMAP_MODULATE)
|
|
lightColor *= shadowValue * (1.0 - u_PrimaryLightAmbient.r) + u_PrimaryLightAmbient.r;
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(USE_LIGHT_VECTOR)
|
|
ambientColor = lightColor;
|
|
float surfNL = clamp(dot(var_Normal.xyz, L), 0.0, 1.0);
|
|
|
|
// reserve 25% ambient to avoid black areas on normalmaps
|
|
lightColor *= 0.75;
|
|
|
|
// Scale the incoming light to compensate for the baked-in light angle
|
|
// attenuation.
|
|
lightColor /= max(surfNL, 0.25);
|
|
|
|
// Recover any unused light as ambient, in case attenuation is over 4x or
|
|
// light is below the surface
|
|
ambientColor = max(ambientColor - lightColor * surfNL, vec3(0.0));
|
|
#else
|
|
ambientColor = var_ColorAmbient.rgb;
|
|
#endif
|
|
|
|
NL = clamp(dot(N, L), 0.0, 1.0);
|
|
NE = clamp(dot(N, E), 0.0, 1.0);
|
|
H = normalize(L + E);
|
|
EH = clamp(dot(E, H), 0.0, 1.0);
|
|
NH = clamp(dot(N, H), 0.0, 1.0);
|
|
|
|
#if defined(USE_SPECULARMAP)
|
|
vec4 specular = texture2D(u_SpecularMap, texCoords);
|
|
#else
|
|
vec4 specular = vec4(1.0);
|
|
#endif
|
|
specular *= u_SpecularScale;
|
|
|
|
#if defined(USE_PBR)
|
|
diffuse.rgb *= diffuse.rgb;
|
|
#endif
|
|
|
|
#if defined(USE_PBR)
|
|
// diffuse rgb is base color
|
|
// specular red is gloss
|
|
// specular green is metallicness
|
|
float gloss = specular.r;
|
|
float metal = specular.g;
|
|
specular.rgb = metal * diffuse.rgb + vec3(0.04 - 0.04 * metal);
|
|
diffuse.rgb *= 1.0 - metal;
|
|
#else
|
|
// diffuse rgb is diffuse
|
|
// specular rgb is specular reflectance at normal incidence
|
|
// specular alpha is gloss
|
|
float gloss = specular.a;
|
|
|
|
// adjust diffuse by specular reflectance, to maintain energy conservation
|
|
diffuse.rgb *= vec3(1.0) - specular.rgb;
|
|
#endif
|
|
|
|
#if defined(GLOSS_IS_GLOSS)
|
|
float roughness = exp2(-3.0 * gloss);
|
|
#elif defined(GLOSS_IS_SMOOTHNESS)
|
|
float roughness = 1.0 - gloss;
|
|
#elif defined(GLOSS_IS_ROUGHNESS)
|
|
float roughness = gloss;
|
|
#elif defined(GLOSS_IS_SHININESS)
|
|
float roughness = pow(2.0 / (8190.0 * gloss + 2.0), 0.25);
|
|
#endif
|
|
|
|
reflectance = CalcDiffuse(diffuse.rgb, NH, EH, roughness);
|
|
|
|
#if defined(r_deluxeSpecular)
|
|
#if defined(USE_LIGHT_VECTOR)
|
|
reflectance += CalcSpecular(specular.rgb, NH, EH, roughness) * r_deluxeSpecular;
|
|
#else
|
|
reflectance += CalcSpecular(specular.rgb, NH, EH, pow(roughness, r_deluxeSpecular));
|
|
#endif
|
|
#endif
|
|
|
|
gl_FragColor.rgb = lightColor * reflectance * (attenuation * NL);
|
|
gl_FragColor.rgb += ambientColor * diffuse.rgb;
|
|
|
|
#if defined(USE_CUBEMAP)
|
|
reflectance = EnvironmentBRDF(roughness, NE, specular.rgb);
|
|
|
|
vec3 R = reflect(E, N);
|
|
|
|
// parallax corrected cubemap (cheaper trick)
|
|
// from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
|
vec3 parallax = u_CubeMapInfo.xyz + u_CubeMapInfo.w * viewDir;
|
|
|
|
#if defined(USE_BOX_CUBEMAP_PARALLAX)
|
|
vec3 cubeLightColor = hitCube(R * u_CubeMapInfo.w, parallax, u_CubeMapInfo.www, ROUGHNESS_MIPS * roughness, u_CubeMap).rgb * u_EnableTextures.w;
|
|
#else
|
|
vec3 cubeLightColor = textureCubeLod(u_CubeMap, R + parallax, ROUGHNESS_MIPS * roughness).rgb * u_EnableTextures.w;
|
|
#endif
|
|
|
|
// normalize cubemap based on last roughness mip (~diffuse)
|
|
// multiplying cubemap values by lighting below depends on either this or the cubemap being normalized at generation
|
|
//vec3 cubeLightDiffuse = max(textureCubeLod(u_CubeMap, N, ROUGHNESS_MIPS).rgb, 0.5 / 255.0);
|
|
//cubeLightColor /= dot(cubeLightDiffuse, vec3(0.2125, 0.7154, 0.0721));
|
|
|
|
#if defined(USE_PBR)
|
|
cubeLightColor *= cubeLightColor;
|
|
#endif
|
|
|
|
// multiply cubemap values by lighting
|
|
// not technically correct, but helps make reflections look less unnatural
|
|
//cubeLightColor *= lightColor * (attenuation * NL) + ambientColor;
|
|
|
|
gl_FragColor.rgb += cubeLightColor * reflectance;
|
|
#endif
|
|
|
|
#if defined(USE_PRIMARY_LIGHT) || defined(SHADOWMAP_MODULATE)
|
|
vec3 L2, H2;
|
|
float NL2, EH2, NH2;
|
|
|
|
L2 = var_PrimaryLightDir.xyz;
|
|
|
|
// enable when point lights are supported as primary lights
|
|
//sqrLightDist = dot(L2, L2);
|
|
//L2 /= sqrt(sqrLightDist);
|
|
|
|
NL2 = clamp(dot(N, L2), 0.0, 1.0);
|
|
H2 = normalize(L2 + E);
|
|
EH2 = clamp(dot(E, H2), 0.0, 1.0);
|
|
NH2 = clamp(dot(N, H2), 0.0, 1.0);
|
|
|
|
reflectance = CalcSpecular(specular.rgb, NH2, EH2, roughness);
|
|
|
|
// bit of a hack, with modulated shadowmaps, ignore diffuse
|
|
#if !defined(SHADOWMAP_MODULATE)
|
|
reflectance += CalcDiffuse(diffuse.rgb, NH2, EH2, roughness);
|
|
#endif
|
|
|
|
lightColor = u_PrimaryLightColor;
|
|
|
|
#if defined(USE_SHADOWMAP)
|
|
lightColor *= shadowValue;
|
|
#endif
|
|
|
|
// enable when point lights are supported as primary lights
|
|
//lightColor *= CalcLightAttenuation(float(u_PrimaryLightDir.w > 0.0), u_PrimaryLightDir.w / sqrLightDist);
|
|
|
|
gl_FragColor.rgb += lightColor * reflectance * NL2;
|
|
#endif
|
|
|
|
#if defined(USE_PBR)
|
|
gl_FragColor.rgb = sqrt(gl_FragColor.rgb);
|
|
#endif
|
|
|
|
#else
|
|
|
|
gl_FragColor.rgb = diffuse.rgb * lightColor;
|
|
|
|
#endif
|
|
|
|
gl_FragColor.a = alpha;
|
|
}
|