speck-renderer
Version:
Browser-based WebGL molecule renderer with the goal of producing figures that are as attractive as they are practical.
162 lines (138 loc) • 4.02 kB
JavaScript
const bondsShader = `\
precision highp float;
attribute vec3 aImposter;
attribute vec3 aPosA;
attribute vec3 aPosB;
attribute float aRadA;
attribute float aRadB;
attribute vec3 aColA;
attribute vec3 aColB;
uniform mat4 uView;
uniform mat4 uProjection;
uniform mat4 uModel;
uniform mat4 uRotation;
uniform float uBondRadius;
uniform float uAtomScale;
uniform float uRelativeAtomScale;
varying vec3 vNormal;
varying vec3 vPosA, vPosB;
varying float vRadA, vRadB;
varying vec3 vColA, vColB;
varying float vRadius;
mat3 alignVector(vec3 a, vec3 b) {
vec3 v = cross(a, b);
float s = length(v);
float c = dot(a, b);
mat3 I = mat3(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
mat3 vx = mat3(
0, v.z, -v.y,
-v.z, 0, v.x,
v.y, -v.x, 0
);
return I + vx + vx * vx * ((1.0 - c) / (s * s));
}
void main() {
vRadius = uBondRadius;
vec3 pos = vec3(aImposter);
// Scale the box in x and z to be bond-radius.
pos = pos * vec3(vRadius, 1, vRadius);
// Shift the origin-centered cube so that the bottom is at the origin.
pos = pos + vec3(0, 1, 0);
// Stretch the box in y so that it is the length of the bond.
pos = pos * vec3(1, length(aPosA - aPosB) * 0.5, 1);
// Find the rotation that aligns vec3(0, 1, 0) with vec3(uPosB - uPosA) and apply it.
vec3 a = normalize(vec3(-0.000001, 1.000001, 0.000001));
vec3 b = normalize(aPosB - aPosA);
mat3 R = alignVector(a, b);
pos = R * pos;
// Shift the cube so that the bottom is centered at the middle of atom A.
pos = pos + aPosA;
vec4 position = uModel * vec4(pos, 1);
gl_Position = uProjection * uView * position;
vPosA = aPosA;
vPosB = aPosB;
vRadA = uAtomScale * (1.0 + (aRadA - 1.0) * uRelativeAtomScale);
vRadB = uAtomScale * (1.0 + (aRadB - 1.0) * uRelativeAtomScale);
vColA = aColA;
vColB = aColB;
}
// __split__
precision highp float;
uniform mat4 uRotation;
uniform vec2 uBottomLeft;
uniform vec2 uTopRight;
uniform float uDepth;
uniform float uRes;
uniform float uBondShade;
uniform int uMode;
varying vec3 vPosA, vPosB;
varying float vRadA, vRadB;
varying vec3 vColA, vColB;
varying float vRadius;
mat3 alignVector(vec3 a, vec3 b) {
vec3 v = cross(a, b);
float s = length(v);
float c = dot(a, b);
mat3 I = mat3(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
mat3 vx = mat3(
0, v.z, -v.y,
-v.z, 0, v.x,
v.y, -v.x, 0
);
return I + vx + vx * vx * ((1.0 - c) / (s * s));
}
void main() {
vec2 res = vec2(uRes, uRes);
vec3 r0 = vec3(uBottomLeft + (gl_FragCoord.xy/res) * (uTopRight - uBottomLeft), uDepth/2.0);
vec3 rd = vec3(0, 0, -1);
vec3 i = normalize(vPosB - vPosA);
i = vec3(uRotation * vec4(i, 0));
vec3 j = normalize(vec3(-0.000001, 1.000001, 0.000001));
mat3 R = alignVector(i, j);
vec3 r0p = r0 - vec3(uRotation * vec4(vPosA, 0));
r0p = R * r0p;
vec3 rdp = R * rd;
float a = dot(rdp.xz, rdp.xz);
float b = 2.0 * dot(rdp.xz, r0p.xz);
float c = dot(r0p.xz, r0p.xz) - vRadius*vRadius;
float disc = b*b - 4.0*a*c;
if (disc <= 0.0) {
discard;
}
float t = (-b - sqrt(disc))/(2.0*a);
if (t < 0.0) {
discard;
}
vec3 coord = r0p + rdp * t;
if (coord.y < 0.0 || coord.y > length(vPosA - vPosB)) {
discard;
}
vec3 color;
if (coord.y < vRadA + 0.5 * (length(vPosA - vPosB) - (vRadA + vRadB))) {
color = vColA;
} else {
color = vColB;
}
color = mix(color, vec3(1,1,1), uBondShade);
R = alignVector(j, i);
vec3 normal = normalize(R * vec3(coord.x, 0, coord.z));
coord = r0 + rd * t;
if (uMode == 0) {
gl_FragColor = vec4(color, 1);
} else if (uMode == 1) {
gl_FragColor = vec4(normal * 0.5 + 0.5, 1.0);
}
gl_FragDepthEXT = -(coord.z - uDepth/2.0)/uDepth;
}`
export default bondsShader;