65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 WebGL 和 HTML5 构建分子 3D 查看器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8投票s)

2012年1月5日

CPOL

3分钟阅读

viewsIcon

59035

downloadIcon

2095

使用 JavaScript 和 HTML5 构建一个 3D 分子查看器。

Sample Image of 3D Viewer

引言

虽然很多人熟悉 OpenGL,但使用 WebGL 的人相对较少。 然而,OpenGL 的许多概念和格式也适用于 WebGL。 本文旨在测试使用 WebGL 编程的难度。 作为一个 WebGL 新手,并且只有很少的 OpenGL 经验,我花了不到 6 个小时就完成了它。 有许多蛋白质查看器以预打包的 Java applet 或 C++ 程序的形式提供。 该项目试图了解使用 JavaScript 和 WebGL 制作一个有多困难。 令人惊讶的是,它非常简单!

背景

WebGL 是 JavaScript 浏览器中图形处理代码的实现,允许浏览器利用 GPU。

Using the Code

将代码安装在 Web 服务器上,无论是本地 Web 服务器还是远程服务器。 转到 index.html,单击“选择文件”按钮,选择计算机上的本地 PDB 文件,然后查看分子。

按键如下:

A: Move Along -X Axis
W: Move Along +Y Axis
S: Move Along -Y Axis
D: Move Along +X Axis
Left Arrow: Move Along -X Axis
Right Arrow:Move Along +X Axis
Down Arrow: Move Along +Z Axis
Up Arrow: Move Along -Z Axis
Shift+A: Tilt to the left 
Shift+W: Look Up
Shift+S: Look Down
Shift+D: Tile to the right
Shift+Left Arrow: Rotate to the Left
Shift+Right Arrow:Rotate to the right
Shift+Down Arrow: Look Down
Shift+Up Arrow: Look UP
Mouse Drag Left: Rotate X
Mouse Drag Right:Rotate X
Mouse Drag Up: Rotate Y
Mouse Drag Down: Rotate Y
Mouse Wheel Forward: Zoom In
Mouse Wheel Back: Zoom Out

一个 PDB 文件示例

COMPND      PROPANE
AUTHOR      DAVE WOODCOCK  95  12 18
ATOM      1  C           1       1.241   0.444   0.349  1.00  0.00
ATOM      2  C           1      -0.011  -0.441   0.333  1.00  0.00
ATOM      3  C           1      -1.176   0.296  -0.332  1.00  0.00
ATOM      4  H           1       1.516   0.699  -0.675  1.00  0.00
ATOM      5  H           1       2.058  -0.099   0.827  1.00  0.00
ATOM      6  H           1       1.035   1.354   0.913  1.00  0.00
ATOM      7  H           1      -0.283  -0.691   1.359  1.00  0.00
ATOM      8  H           1       0.204  -1.354  -0.225  1.00  0.00
ATOM      9  H           1      -0.914   0.551  -1.359  1.00  0.00
ATOM     10  H           1      -1.396   1.211   0.219  1.00  0.00
ATOM     11  H           1      -2.058  -0.345  -0.332  1.00  0.00
TER      12              1
END

第一步是选择一个文件并解析该文件以获取 ATOM 条目。 我们使用文件输入按钮的 onclick 操作符选择一个文件。 接下来,我们设置一个 FileReader 对象并解析该文件。 获得文件内容后,我们调用 var lines = contents.split("\n"); 将文件拆分为行。 接下来,我们必须遍历这些行并使用空格将其拆分为数组。 然后,我们创建一个数组来保存我们的 ATOM 对象,并用行条目填充它。 当我们分隔行时,会有空白的数组条目,因此我们必须循环遍历该行,并且只复制我们想要的值。 此外,有些行比其他行有更多的空格,因此我们不能使用固定的位置。 因此,我们只复制所有非空值。 该过程如下所示:

for (y = 0; y < singleLine.length; y++)//loop through the line
{
	if (singleLine[y]!="" && singleLine[y]!=",")//is this a value or empty?
		objects[object_count][counter++] = singleLine[y];//has a value so lets copy it
}

下一步是循环遍历我们的对象数组并找到原子并绘制它们。 请注意,我们尚未将原子分开,而是复制了**所有**非空值。 所以现在我们需要找到我们想要绘制的 ATOM 结构。 查看上面的文件格式,我们可以看到每个原子的 x、y、z 坐标分别位于第 5th、6th、7th 列中。 代码如下所示:

for (i = 0; i < object_count; ++i) {
	if (objects[i][0] == "ATOM")
	{
		if (setcamera != true)
		//have we set the camera to focus
		//on an atom yet? if not pick the first one
		{
            //look at the 0,0,zPosition of the atom
			g.perspectiveMatrix.lookat(0, 0, objects[i][7], 0, 0, 0, 0, 1, 0);
			setcamera = true;
		}

		//draw one molecule
		drawOne(ctx, 30,
			objects[i][5],//x coord
			objects[i][6],//y coord
			objects[i][7],//z coord
			0.75,
			molTexture);//our texture
	}
}

接下来,我们需要将相机和对象设置在正确的位置。 为此,我们使用模型-视图矩阵并将其设置为按相机进行平移和旋转,然后再次按我们绘制的原子进行平移。 看起来是这样的:

// Add in camera controller's position and rotation
var pos = controller.getPosition();//get the camera position
mvMatrix.translate(pos[0],pos[1],pos[2]);//translate by the camera position if needed
mvMatrix.rotate(controller.getRoll(), 1, 0, 0);//rotate if needed
mvMatrix.rotate(controller.getYaw(), 0, 1, 0);//rotate if needed
mvMatrix.rotate(controller.getPitch(), 0, 0, 1);//rotate if needed
mvMatrix.translate(-x,-y,-z);//set the object in the proper place in view

好的,我们有了绘制原子的矩阵,现在我们绘制原子,在任何最终投影之后

ctx.bindTexture(ctx.TEXTURE_2D, texture);
ctx.drawElements(ctx.TRIANGLES, g.sphere.numIndices, ctx.UNSIGNED_SHORT, 0);

剩下的就是使用按键移动我们的相机

function keyCamera(event){//a key is released
	var cam = controller;
	if(event.shiftKey) {
		switch(event.keyCode) {//determine the key pressed
			case 65://a key
				cam.roll(-Math.PI * 0.25);//tilt to the left
				break;
			case 37://left arrow
				cam.yaw(Math.PI * 0.25);//rotate to the left
				break;
			case 68://d key
				cam.roll(Math.PI * 0.25);//tilt to the right
				break;
			case 39://right arrow
				cam.yaw(-Math.PI * 0.25);//rotate to the right
				break;
			case 83://s key
			case 40://down arrow
				cam.pitch(Math.PI * 0.25);//look down
			break;
			case 87://w key
			case 38://up arrow
				cam.pitch(-Math.PI * 0.25);//look up
			break;
		}
	}
	else {
		var pos = cam.getPosition();
		//alert(pos);
		switch(event.keyCode) {//deterime the key pressed
			case 65://a key
			case 37://left arrow
				cam.setPosition(pos[0]-0.5,pos[1],pos[2]);//move - along the X axis
			break;
			case 68://d key
			case 39://right arrow
				cam.setPosition(pos[0]+0.5,pos[1],pos[2]);//more + along the X axis
			break;
			case 83://s key
				cam.setPosition(pos[0],pos[1]-0.5,pos[2]);//move - along the Y axis (down)
			break;
			case 40://down arrow
				cam.setPosition(pos[0],pos[1],pos[2]+0.5);//move + on the Z axis
			break;
			case 87://w key
				cam.setPosition(pos[0],pos[1]+0.50,pos[2]);//move + on the Y axis (up)
			break;
			case 38://up arrow
				cam.setPosition(pos[0],pos[1],pos[2]-0.5);//move - on the Z axis
			break;
		}
	}
}

要初始化 WebGL,我们使用类似这样的东西:

var gl = initWebGL("molview");
if (!gl) {
	return;
}
var c = document.getElementById("molview");//canvas where we will draw

c.addEventListener('webglcontextlost', handleContextLost, false);
c.addEventListener('webglcontextrestored', handleContextRestored, false);
g.program = simpleSetup(gl, "vshader", "fshader",
					[ "vNormal", "vTexCoord", "vPosition"],
					[ 0, 0, 0, 1 ], 10000);
gl.uniform3f(gl.getUniformLocation(g.program, "lightDir"), 0, 0, 1);
gl.uniform1i(gl.getUniformLocation(g.program, "sampler2d"), 0);

if (g.program) {
	g.u_normalMatrixLoc = gl.getUniformLocation(g.program, "u_normalMatrix");
	g.u_modelViewProjMatrixLoc = gl.getUniformLocation(g.program, "u_modelViewProjMatrix");
}

g.sphere = makeSphere(gl, 1, 30, 30);

// get the images
molTexture = loadImageTexture(gl, "./h1.jpg");

最后是相机,我们只需实现一些东西来跟踪我们的位置和旋转,然后就可以开始了。 相机中的代码太多,无法发布,因此我将由感兴趣的各方查看附加的源代码。 为了保持简洁,我省略了一些功能。 附加的代码具有所需的一切和一些 PDB 文件示例。

关注点

我了解了使用 WebGL 编程是多么的相对容易。 对于在短时间内创建可见内容的一个很好的介绍。 WebGL 和 HTML5 都是我期待将来使用的东西。

历史

版本 0.1:为 PDB 文件实现了基本的 ATOM 查看器。

参考文献

© . All rights reserved.