Files
2026-08-03 18:42:47 +08:00

188 lines
5.0 KiB
GLSL

#ifdef PYMOL_WEBGL_IOS
#extension GL_EXT_frag_depth : require
#endif
#include webgl_header.fs
// cylinder imposter fragment shader
uniform float inv_height;
uniform bool no_flat_caps;
uniform float half_bond;
uniform mat4 g_ProjectionMatrix;
uniform float g_Fog_end;
uniform float g_Fog_scale;
#include anaglyph_header.fs
varying vec3 surface_point ;
varying vec3 axis ;
varying vec3 base ;
varying vec3 end_cyl ;
varying vec3 U ;
varying vec3 V ;
varying float radius;
varying float cap;
varying float inv_sqr_height;
varying vec4 color1;
varying vec4 color2;
varying vec2 bgTextureLookup;
uniform bool lighting_enabled;
#include compute_fog_color.fs
#include compute_color_for_light.fs
/*
* Get the lowest bit from 'bits' and shift 'bits' to the right.
* Equivalent to (if 'bits' would be int):
* bit = bits & 0x1; bits >>= 1; return bit;
*/
bool get_bit_and_shift(inout float bits) {
float bit = mod(bits, 2.0);
bits = (bits - bit) / 2.0;
return bit > 0.5;
}
void main(void)
{
vec3 ray_target = surface_point;
#ifdef ortho
vec3 ray_origin = surface_point;
vec3 ray_direction = vec3(0., 0., 1.);
#else
vec3 ray_origin = vec3(0.);
vec3 ray_direction = normalize(-ray_target);
#endif
// basis is local system of coordinates for the cylinder
mat3 basis = mat3(U, V, axis);
// vectors in cylinder xy-plane
vec2 P = ((ray_target - base) * basis).xy;
vec2 D = (ray_direction * basis).xy;
float radius2 = radius*radius;
// calculate distance to the cylinder from ray origin
float a0 = P.x*P.x + P.y*P.y - radius2;
float a1 = P.x*D.x + P.y*D.y;
float a2 = D.x*D.x + D.y*D.y;
// calculate a dicriminant of the above quadratic equation
float d = a1*a1 - a0*a2;
if (d < 0.0)
// outside of the cylinder
discard;
float dist = (-a1 + sqrt(d))/a2;
// point of intersection on cylinder surface
vec3 new_point = ray_target + dist * ray_direction;
vec3 tmp_point = new_point - base;
vec3 normal = normalize(tmp_point - axis * dot(tmp_point, axis));
/* cap : 4 bits : 1st - frontcap
2nd - endcap
3rd - frontcapround
4th - endcapround
5th - interp
*/
float fcap = cap + .001; // to account for odd rounding issues when setting
// varying cap from attribute a_cap, which is a non-normalized
// GL_UNSIGNED_BYTE
bool frontcap = get_bit_and_shift(fcap);
bool endcap = get_bit_and_shift(fcap);
bool frontcapround = get_bit_and_shift(fcap) && no_flat_caps;
bool endcapround = get_bit_and_shift(fcap) && no_flat_caps;
bool nocolorinterp = !get_bit_and_shift(fcap);
vec4 color;
float ratio = dot(new_point-base, vec3(end_cyl-base)) * inv_sqr_height;
if (isPicking || !lighting_enabled){ // for picking
ratio = step(.5, ratio);
} else if (nocolorinterp){
// determine color of half-bond, possible antialiasing
// based on half_bond, depth, and height
float dp = clamp(-half_bond*new_point.z*inv_height, 0., .5);
ratio = smoothstep(.5 - dp, .5 + dp, ratio);
} else {
ratio = clamp(ratio, 0., 1.);
}
color = mix(color1, color2, ratio);
bool cap_test_base = 0.0 > dot((new_point - base), axis);
bool cap_test_end = 0.0 < dot((new_point - end_cyl), axis);
if (cap_test_base || cap_test_end) {
vec3 thisaxis = -axis;
vec3 thisbase = base;
if (cap_test_end) {
thisaxis = axis;
thisbase = end_cyl;
frontcap = endcap;
frontcapround = endcapround;
}
if (!frontcap)
discard;
if (frontcapround) {
vec3 sphere_direction = thisbase - ray_origin;
float b = dot(sphere_direction, ray_direction);
float pos = b*b + radius2 -dot(sphere_direction, sphere_direction);
if (pos < 0.0)
discard;
float near = sqrt(pos) + b;
new_point = near * ray_direction + ray_origin;
normal = normalize(new_point - thisbase);
} else {
// ray-plane intersection
float dNV = dot(thisaxis, ray_direction);
if (dNV < 0.0)
discard;
float near = dot(thisaxis, thisbase - ray_origin) / dNV;
new_point = ray_direction * near + ray_origin;
// within the cap radius?
if (dot(new_point - thisbase, new_point - thisbase) > radius2)
discard;
normal = thisaxis;
}
}
vec2 clipZW = new_point.z * g_ProjectionMatrix[2].zw +
g_ProjectionMatrix[3].zw;
float depth = 0.5 + 0.5 * clipZW.x / clipZW.y;
// front clipping
if (depth <= 0.0)
discard;
gl_FragDepth = depth;
if (!isPicking && lighting_enabled){
color = ApplyColorEffects(color, depth);
color = ApplyLighting(color, normal);
}
if (isPicking){
gl_FragColor = color;
} else {
float fog = (g_Fog_end + new_point.z) * g_Fog_scale;
gl_FragColor = ApplyFog(color, fog);
PostLightingEffects(depth);
}
}
// vi:expandtab:sw=2