Three.js study

鉴于Unity3D越玩越蛋疼。。麻烦的事情太多,决定抛弃Unity3D入坑three.js,在这里发发学习笔记


阅读本文请开启WebGL
首先是水水的Hello,World.

我们来根据这个代码来分析一下结构

  1. <html>
  2.     <head>
  3.         <title>My first Three.js app</title>
  4.         <style>canvas { width: 100%; height: 100% }</style>
  5.     </head>
  6.     <body>
  7.     <script src="js/three.min.js"></script>
  8.     <script>
  9.     // Our Javascript will go here.
  10.     var scene = new THREE.Scene();
  11.     var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
  12.     var renderer = new THREE.WebGLRenderer();
  13.     renderer.setSize( window.innerWidth, window.innerHeight );
  14.     document.body.appendChild( renderer.domElement );
  15.     var geometry = new THREE.CubeGeometry(1,1,1);
  16.     var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
  17.     var cube = new THREE.Mesh( geometry, material );
  18.     scene.add( cube );
  19.  
  20.     camera.position.= 3;
  21.     camera.position.= 0;
  22.     camera.position.= 0;
  23.     function render()
  24.     {
  25.         cube.rotation.+= 0.1;
  26.         cube.rotation.+= 0.1;
  27.         requestAnimationFrame(render);
  28.         renderer.render(scene, camera);
  29.     }
  30.     render();
  31.     </script>
  32. </body>
  33. </html>

 

我们一点点看哈:

Line 10-12我们新建了三个对象:scene camera render,一看这三个对象我们就知道three.js是一个高层一些的渲染器,因为对比于OpenGL的初始化demo

  1.    //objective-cpp
  2.     NSLog(@"u");
  3.     rad=20;
  4.     cam0=new cam( -rad,0,5,0,0,5,0,0,1);
  5.     NSLog(@"this");
  6.     glMatrixMode (GL_PROJECTION);
  7.     glEnable(GL_DEPTH_TEST);
  8.     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  9.     gluPerspective(40,1.33333,0.01,1000);
  10.     cam0->setcam()

高下立判。

其实说来WebGL和OpenGL是一个层级的,不过我们没有心情去看看WebGL的details。

我们继续看这里是添加了一个dom作为显示WebGL地方,然后设置网格材料神马的。然后一个render函数显示,我们可以在下面查看效果

请不吝去 https://github.com/xuhao1/xuhao1.github.io/tree/master/three 查看源代码。

为了使这个demo更好看一点,我们开始读threejs的docs

比如我们加入一个让摄像机随着物体旋转的小功能,要实现这个功能,相当于我们使得camera的观察物位置不变,而camera得位置更新。

我们查看docs,camera继承自object3d类,而camera自身没有提供一个position 或者location函数,so我们打开object3d的doc

.position

Object's local position.

我们继续查看一下position的doc

Properties

.x

.y

.z

就是他了。

So,我们可以简单地让x随时间变化,,当然,lookat的方向是在Global坐标系的,所以定点即可,但是如果不执行lookat函数,则视角方向会不变,所以有关键部分修改如下

	camera.position.z = 3;
	camera.position.x = 0;
	camera.position.y = 0;
	la=new Date().getTime();
	time=0;
	function render()
	{
		time+=0.01;
		cube.rotation.x =time;
		cube.rotation.y =time;
		camera.position.z=Math.cos(time);
		camera.position.y=Math.sin(time);
		requestAnimationFrame(render);
		renderer.render(scene, camera);
		var now= (new Date()).getTime();
		var t=now-la;
		var fps=1000/t;
		$("#fps").text("FPS:"+Math.round(fps));
		la=now;
	}

So,我们可以看到如下的景象

 

题外话,打开手机测试,it works.不过default的背景颜色在电脑上显示为黑色,手机上为白色

一个Box是不是十分的boring呢,so 我们玩点好玩的比如:一架歼击机。

屏幕快照 2014-07-01 下午6.36.47早年下载的一个J20模型

这个是在Mac Preview里面打开的样子

In Unity3D

屏幕快照 2014-07-01 下午6.45.03

我们来把它嵌入到我们的网页里面吧

我们首先看看如何加载一个模型,按照我们已有的知识(没有的请自行面壁五分钟后google计算机图形学),几乎所有的模型最后都是一堆三角形,所以问题就在于怎么加载这个三角形

注意到

var cube = new THREE.Mesh( geometry, material );

这行代码,我们从材料和几何中新建了一个mesh,翻看Mesh的构造器docs

Constructor

Mesh( geometrymaterial )

geometry — An instance of Geometry.
material — An instance of Material (optional).
ok,我们先只care geometry
我们看到Example
var geometry = new THREE.Geometry();

geometry.vertices.push( new THREE.Vector3( -10,  10, 0 ) );
geometry.vertices.push( new THREE.Vector3( -10, -10, 0 ) );
geometry.vertices.push( new THREE.Vector3(  10, -10, 0 ) );

geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );

geometry.computeBoundingSphere();

以及一个属性

.faces

Array of triangles.
The array of faces describe how each vertex in the model is connected with each other.
To signal an update in this array, Geometry.elementsNeedUpdate needs to be set to true.

Exactly是一堆三角形,不过这个操作过于底层,对于高阶的API肯定有直接面向模型文件的接口(没有的话Github上面也会有,否则自己可以写一个:D)

对于如何从stl等类型文件构建一个3D模型,在OpenGL我们可以如此处理
首先是STL的格式实例


solid CATIA STL ##创建者
  facet normal  1.338878e-002  3.548179e-001 -9.348395e-001 ##三角面片法向量的3个分量。
    outer loop    ##开始一个三角形
      vertex -4.756082e+004 -3.870648e+004 -9.014685e+003  ##第一个点
      vertex -8.777554e+004 -3.924620e+004 -9.795490e+003  ##第二个点
      vertex -5.185493e+004 -3.282061e+004 -6.842207e+003  ##第三个点
    endloop
  endfacet
  facet normal -1.520681e-001 -5.632104e-001 -8.122004e-001
    outer loop
      vertex  2.404278e+005 -1.149134e+004 -8.663523e+003
      vertex  2.134073e+005 -8.691770e+003 -5.545803e+003
      vertex  2.309608e+005 -3.070673e+003 -1.273024e+004
    endloop
  endfacet
...

那么处理就是一个个读三角形,一个个画出来了


//从文件创造网格
stlmodel::stlmodel(char*filepath,physx::PxRigidDynamic* actor):
    list(0),xmodel(actor)
{
    std::ifstream file=std::ifstream(filepath);
    string str;
    while (!file.eof())
    {
        file>>str;
        if(str=="loop")
        {
            triangle temp;
            file>>temp;
            list.push_back(temp); 
        }
    }
}
//绘制
GLuint stlmodel::model()
{
    if(maked==1)
        return ptr;
    maked=1;
    long st=getCurrentTime();
    ptr=glGenLists(1);
    glNewList(ptr,GL_COMPILE);
    glBegin(GL_TRIANGLES);
    {
        for(triangle e:list)
        {
            glColor3f(e.po1.x/1+0.2,e.po1.z/3+0.2,e.po1.y*2+0.2);
            e.gldraw();
        }
    }
    glEnd();
    glEndList();
    long ed=getCurrentTime();
    return ptr;
}
void stlmodel::draw()
{
    updatepos();
    //printf("li:%f %f %f\n",pos.x,pos.y,pos.z );
    glTranslatef(pos.x,pos.y,pos.z);              // 左移 1.5 单位,并移入屏幕 6.0
    glRotatef(angle,ax,ay,az);
    glCallList(model());
    glRotatef(-angle,ax,ay,az);
    glTranslatef(-pos.x,-pos.y,-pos.z);             // 左移 1.5 单位,并移入屏幕 6.0
}
//重载的输入流给一个向量
std::istream& operator>>(std::istream& is,vector3f&objects)
{
    string str;
    is>>str;
    is>>objects.x;
    is>>objects.y;
    is>>objects.z;
    return is;
}
//重载的输入流给一个三角形
std::istream& operator>>(std::istream& is,triangle&objects)
{
    is>>objects.po1;
    is>>objects.po2;
    is>>objects.po3;
    string str;
    is>>str;
    return is;
}

我们只需要simply use就可以了,先用blender转出stl格式,然后再转成json,这样单位转换问题不大,转换请使用

http://threejs.org/editor/

然后我们可以把代码修改如下


	// Our Javascript will go here.
	var scene = new THREE.Scene();
	var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
	var renderer = new THREE.WebGLRenderer();
	renderer.setSize( window.innerWidth*0.8, window.innerHeight*0.8 );
	$("#display").append( renderer.domElement );
	var geometry = new THREE.CubeGeometry(1,1,1);
	var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );

	camera.position.z = 2;
	camera.position.x = 2;
	camera.position.y =	2;
	la=new Date().getTime();
	time=0;
	var manager = new THREE.LoadingManager();
	manager.onProgress = function ( item, loaded, total ) {

		console.log( item, loaded, total );

	};

	var loader = new THREE.JSONLoader( manager );
	loader.load( './models/geo.json', function ( geo ) {
		cube = new THREE.Mesh( geo, material );
		cube.rotation.x =-3.1415926*0.5;
		scene.add( cube );
	} );

	function render()
	{
		time+=0.01;
		//cube.rotation.x =time;
		//cube.rotation.y =time;

		camera.position.x=Math.cos(time)*2;
		camera.position.z=Math.sin(time)*2;
		camera.lookAt( new THREE.Vector3(0,0,0) );

		requestAnimationFrame(render);
		renderer.render(scene, camera);
		var now= (new Date()).getTime();
		var t=now-la;
		var fps=1000/t;
		$("#fps").text("FPS:"+Math.round(fps));
		la=now;
	}
	render();

然后显示如下

诶。。挤地铁去了。。各位看官保重,有事请留言

发表评论

电子邮件地址不会被公开。 必填项已用*标注