molstar
Version:
A comprehensive macromolecular library.
293 lines (242 loc) • 10.2 kB
JavaScript
export const compose_frag = `
precision highp float;
precision highp sampler2D;
uniform sampler2D tShaded;
uniform sampler2D tColor;
uniform sampler2D tNormal;
uniform sampler2D tTransparentColor;
uniform sampler2D tSsaoDepth;
uniform sampler2D tSsaoDepthTransparent;
uniform sampler2D tDepthOpaque;
uniform sampler2D tDepthTransparent;
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 float uDenoiseThreshold;
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;
return 1.0;
}
bool isBackground(const in float depth) {
return depth == 1.0;
}
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;
}
//
// TODO: investigate
// https://interplayoflight.wordpress.com/2022/03/26/raytraced-global-illumination-denoising/
//
// https://github.com/BrutPitt/glslSmartDeNoise
//
// smartDeNoise - parameters
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// sampler2D tex - sampler image / texture
// vec2 uv - actual fragment coord
// float sigma > 0 - sigma Standard Deviation
// float kSigma >= 0 - sigma coefficient
// kSigma * sigma --> radius of the circular kernel
// float threshold - edge sharpening threshold
float NormalWeightStrength = 6.0;
vec4 smartDeNoise(sampler2D tex, vec2 uv) {
float sigma = 3.0;
float kSigma = 2.0;
float threshold = uDenoiseThreshold;
vec4 centrPx = texture2D(tex, uv);
if (threshold == 0.0) return centrPx;
float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0)
float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1.0 / (sqrt(PI) * sigma)
float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0)
float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma)
float zBuff = 0.0;
vec4 aBuff = vec4(0.0);
vec3 normal = texture2D(tNormal, uv).xyz;
for (int x = -6; x <= 6; ++x) {
for (int y = -6; y <= 6; ++y) {
vec2 d = vec2(float(x), float(y));
float blurFactor = exp(-dot(d , d) * invSigmaQx2) * invSigmaQx2PI;
vec2 uvSample = uv + d / uTexSize;
vec3 normalSample = texture2D(tNormal, uvSample).xyz;
float normalW = saturate(dot(normal, normalSample));
normalW = pow(normalW, NormalWeightStrength);
blurFactor *= normalW;
vec4 walkPx = texture2D(tex, uvSample);
vec4 dC = walkPx - centrPx;
float deltaFactor = exp(-dot(dC, dC) * invThresholdSqx2) * invThresholdSqrt2PI * blurFactor;
zBuff += deltaFactor;
aBuff += deltaFactor * walkPx;
}
}
if (zBuff <= EPSILON) {
return centrPx;
}
return aBuff / zBuff;
}
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;
}
}
}
}
void main() {
vec2 coords = gl_FragCoord.xy / uTexSize;
vec4 color = smartDeNoise(tColor, coords);
vec4 color = texture2D(tColor, coords);
float opaqueDepth = getDepthOpaque(coords);
float backgroundViewZ = 2.0 * uFar;
float opaqueSelfViewZ = isBackground(opaqueDepth) ? backgroundViewZ : getViewZ(opaqueDepth);
float fogFactor = smoothstep(uFogNear, uFogFar, abs(opaqueSelfViewZ));
float fogAlpha = 1.0 - fogFactor;
float transparentDepth = 1.0;
bool blendTransparency = true;
vec4 transparentColor = texture2D(tTransparentColor, coords);
transparentDepth = getDepthTransparent(coords);
float alpha = 1.0;
if (!uTransparentBackground) {
// mix opaque objects with background color
color.rgb = mix(color.rgb, uFogColor, fogFactor);
} else {
// pre-multiplied alpha expected for transparent background
alpha = fogAlpha;
color.rgb *= fogAlpha;
}
if (!isBackground(opaqueDepth)) {
float occlusionFactor = getSsao(coords);
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);
transparentColor.rgb = mix(uOcclusionColor * (1.0 - fogFactor), transparentColor.rgb, occlusionFactor);
}
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 {
alpha = 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) {
if (transparentColor.a != 0.0) {
// blending
color = transparentColor + color * (1.0 - transparentColor.a);
alpha = transparentColor.a + alpha * (1.0 - transparentColor.a);
}
}
gl_FragColor = vec4(color.rgb, alpha);
}
`;