本文是一篇简单的webGL+threejs构建web三维视图的入门教程,你可以了解到利用threejs创建简单的三维图形,并且控制图形运动。若有不足,欢迎指出。

本文使用的框架是three.js

github地址:https://github.com/mrdoob/three.js,
官网:http://threejs.org/,
文档:http://threejs.org/docs/,
本文中的示例已上传github,地址:https://github.com/RizzleCi/three.js-demo
 

一、创建场景

我们所见的视图由两个部分共同创建,scene和camera。首先定义一个场景:

var scene = new THREE.Scene();

然后定义一个相机:

var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );

等等,定义相机需要视窗的长宽。现在我要让我的绘图显示在页面的一个区域(<div>)标签中。我们来选中这个元素,获取它的长宽。

var container = document.getElementById('canvasdiv');
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;

这样再加上前面的一行代码,我们就完成了对相机的定义。然后把相机位置设定在z轴上方便观察。

camera.position.set(0,0,10)

现在我们需要一个渲染器把定义好的场景渲染出来。

var renderer = new THREE.WebGLRenderer();

给这个渲染器合适的大小。

renderer.setSize( width, height );

然后将其加入到dom中。

canvasdiv.appendChild( renderer.domElement );

(运行以后发现这其实就是一个canvas元素。其实我们也可以在html中创建canvas元素再将renderer绑定到它上面。

var renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('mainCanvas') });


最后进行渲染。

renderer.render(scene,camera);

这样,就建立了一个简单的3d场景。

二、绘制图形

我将threejs中的物体理解为模型+材料。以一个长方体为例。
创建模型:

var geometry = new THREE.BoxGeometry( 1,2,1 );

定义材料:

var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );

有了这两者,我们就可以构建一个长方体方块了。

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

我们将其添加到场景中显示。

scene.add( cube );

这样,一个三维的长方体就绘制完成了
关于其他形状的绘制,张雯莉的threejs入门指南中介绍的很详细,在此不多赘述。
这部分的完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#canvasdiv{
width: 600px;
height: 600px;
} </style>
<script src="js/three.min.js"></script>
</head>
<body> <div class="main-content" id="canvasdiv"> </div> <script type="text/javascript" >
var container = document.getElementById('canvasdiv')
var scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set(0,0,10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 2,1,1 );
var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
renderer.render(scene,camera);
</script>
</body>
</html>

看上去它只是一个长方形而已,但是它确实是一个立体图形,你可以改变一下camera的位置来观察一下。

camera.position.set( 5,3,10 );


好了,这样看起来是一个立体的长方体了吧。

三、创建3d对象

绘制对象

大多数时候,我们需要讲绘制的图形整合到一起进行控制。此时,我们便需要一个3d对象。
在上面绘制的那个长方体上面再放一个球。

var geometry = new THREE.SphereGeometry( 0.5,100,100 );
var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
var ball = new THREE.Mesh( geometry,material );
ball.position.set( 0,0,1 );
scene.add(ball);

另说一句,默认放置mesh的位置是( 0,0,0 ),和改变相机位置一样,我们可以用ball.position.set方法来改变图形或对象的位置。因此动画也利用这个方法来实现。
然后要把它们整合成一个对象。
首先我们创建一个对象。

var myobj = new THREE.Object3D();

然后把我们画的图形添加到对象里就ok啦。

myobj.add( cube );
myobj.add( ball );

这时候我们已经有了一个3d对象,它包含我们刚刚绘制的长方形和球。于是就没有必要像原来那样把图形一个一个地放置到场景里,只需要把刚刚创建的对象放置到场景里。

scene.add( myobj );

完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#canvasdiv{
width: 600px;
height: 600px;
} </style>
<script src="js/three.min.js"></script> </head>
<body> <div class="main-content" id="canvasdiv"> </div> <script type="text/javascript" >
var container = document.getElementById('canvasdiv')
var scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set( 0,0,10 )
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement ); var geometry = new THREE.BoxGeometry( 2,1,1 );
var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
var cube = new THREE.Mesh( geometry, material );
//scene.add( cube ); var geometry = new THREE.SphereGeometry( 0.5,100,100 );
var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
var ball = new THREE.Mesh( geometry,material );
ball.position.set( 0,0,1 );
//scene.add(ball); var myobj = new THREE.Object3D();
myobj.add( cube );
myobj.add( ball );
scene.add( myobj ); renderer.render(scene,camera);
</script>
</body>
</html>

显示效果:

外部导入.obj文件

threejs支持从外部导入.obj文件,听说这种文件是用3DsMax绘制的,用PS也可以编辑。我们需要引入OBJMTLLoader.js,MTLLoader.js文件;也有一个OBJLoader.js,但利用这个库只能导入模型而不能导入绘制obj时添加的材质,个人感觉不是非常实用,就不做介绍了。这时候,我们需要把文件们放到一个服务器上,否则会出现跨域问题。
为了让图像更明显,我们添加一些光线。

scene.add( new THREE.AmbientLight( 0xffffff ) );

这里,我们通过导入图片来设置这个对象的纹理。

var texture = new THREE.Texture();
var loader = new THREE.ImageLoader( );
loader.load( 'tank.jpg', function ( image ) {
texture.image = image;
texture.needsUpdate = true;
} );

开始导入我们的3D对象!

var loader = new THREE.OBJMTLLoader();
loader.load('tank.obj','tank.mtl',function(object){
tank = object; object.traverse(function(child){
if (child instanceof THREE.Mesh){
//将贴图赋于材质
child.material.map = texture;
child.material.transparent = true;
}
});
object.position.set(0,0,0);
scene.add( object );
camera.lookAt( object.position );
renderer.render( scene,camera );
});


模型导入进去了,但是看起来还是很奇怪,这就要我们加一些其他光线渲染一下。在这里我们添加平行光线。

var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.5 );
directionalLight.position.set( 1, 1, 1 );
scene.add( directionalLight );


变得光泽多了。

从外部导入obj的完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#canvasdiv{
width: 1200px;
height: 800px;
} </style>
<script src="js/three.min.js"></script>
<script src="js/MTLLoader.js"></script>
<script src="js/OBJMTLLoader.js"></script>
</head>
<body> <div class="main-content" id="canvasdiv"> </div> <script type="text/javascript" >
var container = document.getElementById('canvasdiv')
var scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set( -10,10,10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement ); var texture = new THREE.Texture();
var loader = new THREE.ImageLoader( );
loader.load( 'tank.jpg', function ( image ) { texture.image = image;
texture.needsUpdate = true; } );
var loader = new THREE.OBJMTLLoader();
loader.load('tank.obj','tank.mtl',function(object){
tank = object;
object.traverse(function(child){
if (child instanceof THREE.Mesh){
//将贴图赋于材质
child.material.map = texture;
//重点,没有该句会导致PNG无法正确显示透明效果
child.material.transparent = true;
}
});
object.position.set(0,0,0);
scene.add( object );
camera.lookAt( object.position );
renderer.render( scene,camera );
});
scene.add( new THREE.AmbientLight( 0xffffff ) );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.5 );
directionalLight.position.set( 1, 1, 1 )
scene.add( directionalLight ); renderer.render(scene,camera);
</script>
</body>
</html>

四、动画

现在我们想办法让这些图形动起来。
在threejs中运用最多的动画是用requestAnimationFrame()方法。也可以利用传统的setInterval()做,但用这个会掉帧。
这里我们做一个render函数,来进行渲染和动画调用。这里以前面添加了myobj对象的代码为基础。
现在来不断地改变对象的角度方便对其进行观察。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#canvasdiv{
width: 600px;
height: 600px;
} </style>
<script src="js/three.min.js"></script> </head>
<body> <div class="main-content" id="canvasdiv"> </div> <script type="text/javascript" >
var container,camera,scene,renderer,myobj; var init = function () {
container = document.getElementById('canvasdiv')
scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set( 0,0,10 )
renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement ); var geometry = new THREE.BoxGeometry( 2,1,1 );
var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
var cube = new THREE.Mesh( geometry, material );
//scene.add( cube ); var geometry = new THREE.SphereGeometry( 0.5,100,100 );
var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
var ball = new THREE.Mesh( geometry,material );
ball.position.set( 0,0,1 );
//scene.add(ball); myobj = new THREE.Object3D();
myobj.add( cube );
myobj.add( ball );
scene.add( myobj );
} var render = function () {
requestAnimationFrame( render );
myobj.rotation.x+=0.01;
myobj.rotation.y+=0.01;
renderer.render(scene,camera);
}
init()
render()
</script>
</body>
</html>

接着让我们对动画进行控制。
在我实现的项目中,是通过websocket连接后台传入参数来控制对象运动的,这里就介绍一下使用参数控制吧。
这里有一个问题,就是requestAnimationFrame回调的函数不能带有参数,否则会出现奇怪的bug。所以我选择用一个全局对象来进行控制。

var control={
s:0,
p:0,
q:0,
j:0,
}

这里s是运动的速度,p,q,j分别是myobj将要运动到的位置的x,y,z坐标。我们先写一个控制它在x轴上运动的函数:

var run = function () {
if ( myobj.position.x<control.p ) {
myobj.position.x += control.s;
requestAnimationFrame( run );
renderer.render( scene,camera )
};
if ( myobj.position.x>control.p ) {
myobj.position.x -= control.s;
requestAnimationFrame( run );
renderer.render( scene,camera )
};
}

再在render函数中添加对run的调用requestAnimationFrame( run )。这样就可以在命令行中改变对象control的值实现控制myobj的运动。但是在运动停止后我的浏览器为什么会变得很卡,而且运动速度回有变化。我还不知道原因。不知道有没有朋友和我有同样的问题。于是我把函数拆成了两个,这样浏览器性能好些。

var run = function () {

    if ( myobj.position.x<control.p ) {
myobj.position.x += control.s;
requestAnimationFrame( run );
};
renderer.render( scene,camera )
}
var runx = function () { if ( myobj.position.x>control.p ) {
myobj.position.x -= control.s;
requestAnimationFrame( runx );
};
renderer.render( scene,camera )
}

同样的,也可以写出在y,z轴上运动的函数。

在x轴上运动的完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#canvasdiv{
width: 600px;
height: 600px;
} </style>
<script src="js/three.min.js"></script> </head>
<body> <div class="main-content" id="canvasdiv"> </div> <script type="text/javascript" >
var container,camera,scene,renderer,myobj; var init = function () {
container = document.getElementById('canvasdiv')
scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set( 0,0,10 )
renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement ); var geometry = new THREE.BoxGeometry( 2,1,1 );
var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
var cube = new THREE.Mesh( geometry, material );
//scene.add( cube ); var geometry = new THREE.SphereGeometry( 0.5,100,100 );
var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
var ball = new THREE.Mesh( geometry,material );
ball.position.set( 0,0,1 );
//scene.add(ball); myobj = new THREE.Object3D();
myobj.add( cube );
myobj.add( ball );
scene.add( myobj );
} var control={
s:0,
p:0,
q:0,
j:0,
} var run = function () { if ( myobj.position.x<control.p ) {
myobj.position.x += control.s;
requestAnimationFrame( run );
};
renderer.render( scene,camera )
}
var runx = function () { if ( myobj.position.x>control.p ) {
myobj.position.x -= control.s;
requestAnimationFrame( runx );
};
renderer.render( scene,camera )
} var render = function () {
requestAnimationFrame( run );
requestAnimationFrame( runx );
renderer.render(scene,camera);
} init()
render()
</script>
</body>
</html>

这个入门教程就到这里了,感谢阅读。

threejs构建web三维视图入门教程的更多相关文章

  1. spring mvc构建WEB应用程序入门例子

    在使用spring mvc 构建web应用程序之前,需要了解spring mvc 的请求过程是怎样的,然后记录下如何搭建一个超简单的spring mvc例子. 1) spring mvc的请求经历 请 ...

  2. JMeter Web测试快速入门教程

    学习前的准备 学习本教程前,你的电脑上至少应该有Apache JMeter这款软件.如果你没有,点击此处下载. 当你点进去后,你会发现它是一个依赖Java的软件 因此如果你电脑上没有Java环境,你应 ...

  3. Web三维编程入门总结之一:WebGL与Threejs入门知识

    /*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识 ...

  4. Web三维编程入门总结之二:面向对象的基础Web3D框架

    本篇主要通过分析Tony Parisi的sim.js库(原版代码托管于:https://github.com/tparisi/WebGLBook/tree/master/sim),总结基础Web3D框 ...

  5. Web三维编程入门总结之三:3D碰撞检测初探

    自己动手写一个方法比分析他人的写的方法困难很多,由此而来的对程序的进一步理解也是分析别人的代码很难得到的. 一.先来几张效果图: 1.场景中有两个半径为1的球体,蓝色线段从球心出发指向球体的“正向” ...

  6. DotNetBrowser入门教程(更新完善中)

    DotNetBrowser 希望实现的目标:桌面软件可以完美运行Html5,内置支持MVC与WebSocket的微型服务器. 基于.Net 4.0开发.开发环境:VS2017,运行环境支持Window ...

  7. Spring Boot入门教程1、使用Spring Boot构建第一个Web应用程序

    一.前言 什么是Spring Boot?Spring Boot就是一个让你使用Spring构建应用时减少配置的一个框架.约定优于配置,一定程度上提高了开发效率.https://zhuanlan.zhi ...

  8. ASP.NET Core 入门教程 1、使用ASP.NET Core 构建第一个Web应用

    一.前言 1.本文主要内容 Visual Studio Code 开发环境配置 使用 ASP.NET Core 构建Web应用 ASP.NET Core Web 应用启动类说明 ASP.NET Cor ...

  9. abp学习(四)——根据入门教程(aspnetMVC Web API进一步学习)

    Introduction With AspNet MVC Web API EntityFramework and AngularJS 地址:https://aspnetboilerplate.com/ ...

随机推荐

  1. redis中set命令的源码分析

    首先在源码中的redis.c文件中有一个结构体:redisCommand redisCommandTable[],这个结构体中定义了每个命令对应的函数,源码中的set命令对应的函数是setComman ...

  2. Eclipse启动报错:An internal error occurred during: "Initializing Java Tooling".

    An internal error occurred during: "Initializing Java Tooling".java.lang.NullPointerExcept ...

  3. Java中的显示锁 ReentrantLock 和 ReentrantReadWriteLock

    在Java1.5中引入了两种显示锁,分别是可重入锁ReentrantLock和可重入读写锁ReentrantReadWriteLock.它们分别实现接口Lock和ReadWriteLock.(注意:s ...

  4. 动态规划--模板--hdu 1059 Dividing

    Dividing Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  5. db2 常用命令(一)

    DB2数据库常用命令小结   ========操作数据库命令==========   -- 启动数据库实例 #db2start    -- 停止数据库实例    #db2sto         # 如 ...

  6. 清空select内容

    select1.options.length= 0 ; 清空 <ul id="ul"> <li>111</li> <li>111&l ...

  7. 【软件使用】GitHub使用教程for VS2012

    一直以来都想使用Git来管理自己平时积累的小代码,就是除了工作之外的代码了.有时候自己搞个小代码,在公司写了,就要通过U盘或者网盘等等一系列工具进行Copy,然后回家才能继续在原来的基础上作业.Cop ...

  8. 怎么在ZBrush中通过遮罩得到子物体

    ZBrush® 中通过遮罩为模型添加子物体的方法简单且方便,我们可以通过按住Ctrl键绘制遮罩结合相关命令创建具有抽出厚度的模型提取出作为子物体附在模型表面.本文将详细介绍在Zbrush中如何通过遮罩 ...

  9. css中position属性(absolute|relative|static|fixed)概述及应用

    position属性的相关定义: static:无特殊定位,对象遵循正常文档流; relative:对象遵循正常文档流; absolute:对象脱离正常文档流 fixed:对象脱离正常文档流 我们先来 ...

  10. java 20 - 9 带有缓冲区的字节输出流和字节输入流

    由之前字节输入的两个方式,我们可以发现,通过定义数组读取数组的方式比一个个字节读取的方式快得多. 所以,java就专门提供了带有缓冲区的字节类: 缓冲区类(高效类) 写数据:BufferedOutpu ...