playcanvas
Version:
PlayCanvas WebGL game engine
62 lines (49 loc) • 2 kB
JavaScript
var reflectionEnvPS = /* glsl */ `
uniform sampler2D texture_envAtlas;
uniform float material_reflectivity;
// calculate mip level for shiny reflection given equirect coords uv.
float shinyMipLevel(vec2 uv) {
vec2 dx = dFdx(uv);
vec2 dy = dFdy(uv);
// calculate second dF at 180 degrees
vec2 uv2 = vec2(fract(uv.x + 0.5), uv.y);
vec2 dx2 = dFdx(uv2);
vec2 dy2 = dFdy(uv2);
// calculate min of both sets of dF to handle discontinuity at the azim edge
float maxd = min(max(dot(dx, dx), dot(dy, dy)), max(dot(dx2, dx2), dot(dy2, dy2)));
return clamp(0.5 * log2(maxd) - 1.0 + textureBias, 0.0, 5.0);
}
vec3 calcReflection(vec3 reflDir, float gloss) {
vec3 dir = cubeMapProject(reflDir) * vec3(-1.0, 1.0, 1.0);
vec2 uv = toSphericalUv(dir);
// calculate roughness level
float level = saturate(1.0 - gloss) * 5.0;
float ilevel = floor(level);
// accessing the shiny (top level) reflection - perform manual mipmap lookup
float level2 = shinyMipLevel(uv * atlasSize);
float ilevel2 = floor(level2);
vec2 uv0, uv1;
float weight;
if (ilevel == 0.0) {
uv0 = mapShinyUv(uv, ilevel2);
uv1 = mapShinyUv(uv, ilevel2 + 1.0);
weight = level2 - ilevel2;
} else {
// accessing rough reflection - just sample the same part twice
uv0 = uv1 = mapRoughnessUv(uv, ilevel);
weight = 0.0;
}
vec3 linearA = {reflectionDecode}(texture2D(texture_envAtlas, uv0));
vec3 linearB = {reflectionDecode}(texture2D(texture_envAtlas, uv1));
vec3 linear0 = mix(linearA, linearB, weight);
vec3 linear1 = {reflectionDecode}(texture2D(texture_envAtlas, mapRoughnessUv(uv, ilevel + 1.0)));
return processEnvironment(mix(linear0, linear1, level - ilevel));
}
void addReflection(vec3 reflDir, float gloss) {
dReflection += vec4(calcReflection(reflDir, gloss), material_reflectivity);
}
`;
export { reflectionEnvPS as default };