molstar
Version:
A comprehensive macromolecular library.
223 lines (194 loc) • 8.14 kB
JavaScript
/**
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
* @author Gianluca Tomasello <giagitom@gmail.com>
*/
export const postprocessing_frag = `
precision highp float;
precision highp int;
precision highp sampler2D;
uniform sampler2D tSsaoDepth;
uniform sampler2D tSsaoDepthTransparent;
uniform sampler2D tColor;
uniform sampler2D tTransparentColor;
uniform sampler2D tDepthOpaque;
uniform sampler2D tDepthTransparent;
uniform sampler2D tShadows;
uniform sampler2D tOutlines;
uniform vec2 uTexSize;
uniform float uNear;
uniform float uFar;
uniform float uFogNear;
uniform float uFogFar;
uniform vec3 uFogColor;
uniform vec3 uOutlineColor;
uniform vec3 uOcclusionColor;
uniform bool uTransparentBackground;
uniform vec2 uOcclusionOffset;
float getViewZ(const in float depth) {
return orthographicDepthToViewZ(depth, uNear, uFar);
return perspectiveDepthToViewZ(depth, uNear, uFar);
}
float getDepthOpaque(const in vec2 coords) {
return texture2D(tDepthOpaque, coords).r;
return unpackRGBAToDepth(texture2D(tDepthOpaque, coords));
}
float getDepthTransparent(const in vec2 coords) {
return unpackRGBAToDepthWithAlpha(texture2D(tDepthTransparent, coords)).x;
}
bool isBackground(const in float depth) {
return depth == 1.0;
}
int squaredOutlineScale = dOutlineScale * dOutlineScale;
void getOutline(const in vec2 coords, out bool hasOpaque, out bool hasTransparent, out float opaqueDepth, out float transparentDepth, out float alpha) {
vec2 invTexSize = 1.0 / uTexSize;
hasOpaque = false;
hasTransparent = false;
opaqueDepth = 1.0;
transparentDepth = 1.0;
alpha = 0.0;
for (int y = -dOutlineScale; y <= dOutlineScale; y++) {
for (int x = -dOutlineScale; x <= dOutlineScale; x++) {
if (x * x + y * y > squaredOutlineScale) {
continue;
}
vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize;
vec4 sampleOutlineCombined = texture2D(tOutlines, sampleCoords);
float sampleOpaqueDepth = unpackRGToUnitInterval(sampleOutlineCombined.gb);
float sampleTransparentDepth = sampleOutlineCombined.a;
vec2 sampleFlagWithAlpha = unpack2x4(sampleOutlineCombined.r);
float sampleFlag = sampleFlagWithAlpha.x;
float sampleAlpha = clamp(sampleFlagWithAlpha.y * 0.5, 0.01, 1.0);
if ((sampleFlag > 0.20 && sampleFlag < 0.30) || (sampleFlag > 0.70 && sampleFlag < 0.80)) { // transparent || both
if (sampleOpaqueDepth < opaqueDepth) {
hasOpaque = true;
opaqueDepth = sampleOpaqueDepth;
}
}
if ((((sampleFlag > 0.45 && sampleFlag < 0.55) || (sampleFlag > 0.70 && sampleFlag < 0.80))) && sampleTransparentDepth < transparentDepth) { // transparent || both
hasTransparent = true;
transparentDepth = sampleTransparentDepth;
alpha = sampleAlpha;
}
}
}
}
float getSsao(vec2 coords) {
float rawSsao = unpackRGToUnitInterval(texture2D(tSsaoDepth, coords).xy);
if (rawSsao > 0.999) {
return 1.0;
} else if (rawSsao > 0.001) {
return rawSsao;
}
// treat values close to 0.0 as errors and return no occlusion
return 1.0;
}
float getSsaoTransparent(vec2 coords) {
float rawSsao = unpackRGToUnitInterval(texture2D(tSsaoDepthTransparent, coords).xy);
if (rawSsao > 0.999) {
return 1.0;
} else if (rawSsao > 0.001) {
return rawSsao;
}
// treat values close to 0.0 as errors and return no occlusion
return 1.0;
}
void main(void) {
vec2 coords = gl_FragCoord.xy / uTexSize;
vec4 color = texture2D(tColor, coords);
float opaqueDepth = getDepthOpaque(coords);
float transparentDepth = 1.0;
bool blendTransparency = true;
vec4 transparentColor = texture2D(tTransparentColor, coords);
transparentDepth = getDepthTransparent(coords);
bool isOpaqueBackground = isBackground(opaqueDepth);
float viewDist = abs(getViewZ(opaqueDepth));
float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
if (!isOpaqueBackground) {
float occlusionFactor = getSsao(coords + uOcclusionOffset);
if (!uTransparentBackground) {
color.rgb = mix(mix(uOcclusionColor, uFogColor, fogFactor), color.rgb, occlusionFactor);
} else {
color.rgb = mix(uOcclusionColor * (1.0 - fogFactor), color.rgb, occlusionFactor);
}
}
if (!isBackground(transparentDepth)) {
float viewDist = abs(getViewZ(transparentDepth));
float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
float occlusionFactor = getSsaoTransparent(coords + uOcclusionOffset);
transparentColor.rgb = mix(uOcclusionColor * (1.0 - fogFactor), transparentColor.rgb, occlusionFactor);
}
if (!isOpaqueBackground) {
vec4 shadow = texture2D(tShadows, coords);
if (!uTransparentBackground) {
color.rgb = mix(mix(vec3(0), uFogColor, fogFactor), color.rgb, shadow.a);
} else {
color.rgb = mix(vec3(0) * (1.0 - fogFactor), color.rgb, shadow.a);
}
}
// outline needs to be handled after occlusion and shadow to keep them clean
bool hasOpaque;
bool hasTransparent;
float outlineOpaqueDepth;
float outlineTransparentDepth;
float outlineAlpha;
getOutline(coords, hasOpaque, hasTransparent, outlineOpaqueDepth, outlineTransparentDepth, outlineAlpha);
if (hasOpaque) {
float viewDist = abs(getViewZ(outlineOpaqueDepth));
float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
if (!uTransparentBackground) {
color.rgb = mix(uOutlineColor, uFogColor, fogFactor);
} else {
color.a = 1.0 - fogFactor;
color.rgb = mix(uOutlineColor, vec3(0.0), fogFactor);
}
}
if (hasTransparent) {
if (hasOpaque && outlineOpaqueDepth < outlineTransparentDepth) {
blendTransparency = false;
} else {
float finalOutlineAlpha = clamp(outlineAlpha * 2.0, 0.0, 1.0);
float viewDist = abs(getViewZ(outlineTransparentDepth));
float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
float finalAlpha = max(transparentColor.a, finalOutlineAlpha * (1.0 - fogFactor));
transparentColor.a = finalAlpha;
transparentColor.rgb = uOutlineColor * finalAlpha;
}
}
if (blendTransparency) {
float alpha = transparentColor.a;
if (alpha != 0.0) {
// blending
color = transparentColor + color * (1.0 - alpha);
}
}
gl_FragColor = color;
}
`;