Skip to content

Commit

Permalink
use spherical billboards on systems that support it
Browse files Browse the repository at this point in the history
Instead of creating full-blown sphere geometries for every atom, render
the spheres using spherical billboards. This reduces the geometry of
every atom to 4 vertices and improves rendering times by an order of
magnitude. The spheres also look much, much better.

Spherical billboards only work on systems that support the EXT_frag_depth
extension. On other systems, we still render everything using
triangulated spheres.

Picking and selection highlighting do not work yet. This will follow
in a one of the next commits.
  • Loading branch information
biasmv committed Jul 26, 2015
1 parent 3e47c93 commit dcf7ca7
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 4 deletions.
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ SOURCE_FILES = [
'src/gfx/label.js',
'src/gfx/line-geom.js',
'src/gfx/mesh-geom.js',
'src/gfx/billboard-geom.js',
'src/gfx/render.js',
'src/gfx/scene-node.js',
'src/gfx/shaders.js',
Expand Down
2 changes: 1 addition & 1 deletion demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function load(pdb_id) {
$.ajax({ url : 'pdbs/'+pdb_id+'.pdb', success : function(data) {
structure = io.pdb(data);
//mol.assignHelixSheet(structure);
cartoon();
spheres();
viewer.autoZoom();
}});
}
Expand Down
64 changes: 64 additions & 0 deletions src/gfx/billboard-geom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) 2013-2015 Marco Biasini
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

define(
[
'../utils',
'./mesh-geom',
],
function(
utils,
MeshGeom
) {

"use strict";

function BillboardGeom(gl, float32Allocator, uint16Allocator) {
MeshGeom.call(this, gl, float32Allocator, uint16Allocator);
}

utils.derive(BillboardGeom, MeshGeom, {
draw : function(cam, shaderCatalog, style, pass) {
// we need the back-faces for the outline rendering
this._gl.disable(this._gl.CULL_FACE);
MeshGeom.prototype.draw.call(this, cam, shaderCatalog, style, pass);
this._gl.enable(this._gl.CULL_FACE);
},
shaderForStyleAndPass :
function(shaderCatalog, style, pass) {
if (pass === 'normal') {
return shaderCatalog.spheres;
}
if (pass === 'outline') {
return shaderCatalog.outlineSpheres;
}
return null;
/*
if (pass === 'select') {
return shaderCatalog.selectSpheres;
}
*/
},
});

return BillboardGeom;

});

52 changes: 52 additions & 0 deletions src/gfx/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ define(
'./geom-builders',
'./mesh-geom',
'./line-geom',
'./billboard-geom',
'./vert-assoc',
'../color'
],
Expand All @@ -34,6 +35,7 @@ define(
geomBuilders,
MeshGeom,
LineGeom,
BillboardGeom,
vertAssoc,
color) {
"use strict";
Expand Down Expand Up @@ -147,6 +149,56 @@ exports.spheres = function(structure, gl, opts) {
return geom;
};

var billboardedSpheresForChain = (function() {
var color = vec4.fromValues(0.0, 0.0, 0.0, 1.0);

return function(meshGeom, vertAssoc, opts, chain) {
var atomCount = chain.atomCount();
var idRange = opts.idPool.getContinuousRange(atomCount);
meshGeom.addIdRange(idRange);
var vertsPerSphere = 4; // one quad per sphere
var indicesPerSphere = 6; // two triangles per quad
var radius = 1.5 * opts.radiusMultiplier;
meshGeom.addChainVertArray(chain, vertsPerSphere*atomCount,
indicesPerSphere*atomCount);
chain.eachAtom(function(atom) {
var va = meshGeom.vertArrayWithSpaceFor(vertsPerSphere);
opts.color.colorFor(atom, color, 0);
var objId = idRange.nextId({ geom: meshGeom, atom : atom });
var vertStart = va.numVerts();
// store center of quad in normal, so we can use a standard indexed
// vertex array.
var p = atom.pos();
va.addVertex([p[0] - radius, p[1] - radius, p[2]], p, color, objId);
va.addVertex([p[0] + radius, p[1] + radius, p[2]], p, color, objId);
va.addVertex([p[0] + radius, p[1] - radius, p[2]], p, color, objId);
va.addVertex([p[0] - radius, p[1] + radius, p[2]], p, color, objId);
va.addTriangle(vertStart + 0, vertStart + 1, vertStart + 2);
va.addTriangle(vertStart + 0, vertStart + 3, vertStart + 1);
var vertEnd = va.numVerts();
vertAssoc.addAssoc(atom, va, vertStart, vertEnd);
});
};
})();

exports.billboardedSpheres = function(structure, gl, opts) {
console.time('billboardedSpheres');
var geom = new BillboardGeom(gl, opts.float32Allocator,
opts.uint16Allocator);
var vertAssoc = new AtomVertexAssoc(structure, true);
geom.addVertAssoc(vertAssoc);
geom.setShowRelated(opts.showRelated);
opts.color.begin(structure);
structure.eachChain(function(chain) {
billboardedSpheresForChain(geom, vertAssoc, opts, chain);
});
opts.color.end(structure);
console.timeEnd('billboardedSpheres');
return geom;
};




var ballsAndSticksForChain = (function() {
var midPoint = vec3.create(), dir = vec3.create();
Expand Down
90 changes: 90 additions & 0 deletions src/gfx/shaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,5 +304,95 @@ void main() { \n\
if (gl_FragColor.a == 0.0) { discard; }\n\
}',

// spherical billboard fragment shader
OUTLINE_SPHERES_FS : '\n\
#extension GL_EXT_frag_depth : enable\n\
precision ${PRECISION} float;\n\
\n\
varying vec2 vertTex;\n\
varying vec4 vertCenter;\n\
varying vec4 vertColor;\n\
uniform float fogNear;\n\
uniform float fogFar;\n\
uniform vec3 outlineColor;\n\
uniform vec3 fogColor;\n\
uniform bool fog;\n\
uniform mat4 projectionMat;\n\
\n\
void main(void) {\n\
if (vertTex.x*vertTex.x+vertTex.y*vertTex.y > 0.5)\n\
discard;\n\
vec3 pos = vec3(vertTex.x, vertTex.y, \n\
1.0*1.0-vertTex.x*vertTex.x-vertTex.y*vertTex.y);\n\
vec3 normal = normalize(pos);\n\
if (normal.z > 0.7) { \n\
discard; \n\
} \n\
vec4 projected = projectionMat * (vertCenter + vec4(pos, 1.0));\n\
float depth = projected.z / projected.w;\n\
gl_FragDepthEXT = depth;\n\
gl_FragColor = vec4(outlineColor, vertColor.a);\n\
if (fog) {\n\
float fog_factor = smoothstep(fogNear, fogFar, depth);\n\
gl_FragColor = mix(gl_FragColor, vec4(fogColor, gl_FragColor.w),\n\
fog_factor);\n\
}\n\
}',
// spherical billboard fragment shader
SPHERES_FS : '\n\
#extension GL_EXT_frag_depth : enable\n\
precision ${PRECISION} float;\n\
\n\
varying vec2 vertTex;\n\
varying vec4 vertCenter;\n\
varying vec4 vertColor;\n\
uniform float fogNear;\n\
uniform float fogFar;\n\
uniform vec3 fogColor;\n\
uniform bool fog;\n\
uniform mat4 projectionMat;\n\
\n\
void main(void) {\n\
if (vertTex.x*vertTex.x+vertTex.y*vertTex.y > 0.48)\n\
discard;\n\
vec3 pos = vec3(vertTex.x, vertTex.y, \n\
1.0*1.0-vertTex.x*vertTex.x-vertTex.y*vertTex.y);\n\
vec3 normal = normalize(pos);\n\
float dp = dot(normal, vec3(0.0, 0.0, 1.0))*0.5+0.5;\n\
float hemi = max(0.0, dp);\n\
vec4 projected = projectionMat * (vertCenter + vec4(pos, 1.0));\n\
float depth = projected.z / projected.w;\n\
gl_FragDepthEXT = depth;\n\
gl_FragColor = vec4(vertColor.rgb*hemi, vertColor.a);\n\
if (fog) {\n\
float fog_factor = smoothstep(fogNear, fogFar, depth);\n\
gl_FragColor = mix(gl_FragColor, vec4(fogColor, gl_FragColor.w),\n\
fog_factor);\n\
}\n\
}',

SPHERES_VS : '\n\
precision ${PRECISION} float;\n\
attribute vec3 attrPos;\n\
attribute vec4 attrColor;\n\
attribute vec3 attrNormal;\n\
\n\
uniform mat4 projectionMat;\n\
uniform mat4 modelviewMat;\n\
uniform mat4 rotationMat;\n\
varying vec4 vertColor;\n\
varying vec2 vertTex;\n\
varying vec4 vertCenter;\n\
void main() {\n\
vec3 d = attrPos - attrNormal;\n\
vec4 rotated = vec4(d, 0.0)*rotationMat;\n\
//vec4 rotated = vec4(d, 0.0);\n\
gl_Position = projectionMat * modelviewMat * \n\
(vec4(attrNormal, 1.0)+rotated);\n\
vertTex = normalize(d).xy;\n\
vertColor = attrColor;\n\
vertCenter = modelviewMat* vec4(attrNormal, 1.0);\n\
}'

});

18 changes: 15 additions & 3 deletions src/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,12 @@ Viewer.prototype = {
shaders.SELECT_LINES_FS, p),
select : c.initShader(shaders.SELECT_VS, shaders.SELECT_FS, p)
};

if (c.gl().getExtension('EXT_frag_depth')) {
this._shaderCatalog.spheres = c.initShader(shaders.SPHERES_VS,
shaders.SPHERES_FS, p);
this._shaderCatalog.outlineSpheres = c.initShader(shaders.SPHERES_VS,
shaders.OUTLINE_SPHERES_FS, p);
}
this._boundDraw = utils.bind(this, this._draw);
this._touchHandler = new TouchHandler(this._canvas.domElement(),
this, this._cam);
Expand Down Expand Up @@ -669,8 +674,15 @@ Viewer.prototype = {
options.color = options.color || color.byElement();
options.sphereDetail = this.options('sphereDetail');
options.radiusMultiplier = options.radiusMultiplier || 1.0;

var obj = render.spheres(structure, this._canvas.gl(), options);
var obj;
// in case we can write to the depth buffer from the fragment shader
// (EXT_frag_depth) we can use billboarded spheres instead of creating
// the full sphere geometry. That's faster AND looks better.
if (this._canvas.gl().getExtension('EXT_frag_depth')) {
obj = render.billboardedSpheres(structure, this._canvas.gl(), options);
} else {
obj = render.spheres(structure, this._canvas.gl(), options);
}
return this.add(name, obj);
},

Expand Down

0 comments on commit dcf7ca7

Please sign in to comment.