-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathcanvas_materials.html
359 lines (311 loc) · 15.8 KB
/
canvas_materials.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - materials</title>
<meta charset="utf-8">
<!--
如果没有设置viewport的width的话,网页很可能会超出手机屏幕宽度,具体多宽,要看浏览器定义的默认宽度是多少
user-scalable=no,规定了用户不能缩放网页,但有些浏览器对该项支持不是很好,故需要设置minimum-scale和maximum-scale相同来限制用户缩放
-->
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #202020;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/three.js"></script>
<!--
想要使用CanvasRenderer,必须添加如下两个js文件
Projector.js顾名思义上将3d图像投影到Canvas("2d")上,如果没有该文件会报如下错误
THREE.Projector has been moved to /examples/js/renderers/Projector.js. three.js:42883:3
TypeError: THREE.RenderableVertex is not a constructor
-->
<script src="js/renderers/Projector.js"></script>
<script src="js/renderers/CanvasRenderer.js"></script>
<!--
统计插件(FPS,渲染时间,chrome内存使用率),min表示js代码经过压缩
-->
<script src="js/libs/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, renderer, objects;
var pointLight;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
/*
透视相机
PerspectiveCamera(fov, aspect, near, far)
fov(视场):从相机位置能够看到的部分场景。推荐默认值45
aspect(长宽比):渲染结果输出区域的横向长度和纵向长度的比值。推荐默认值window.innerWidth/window.innerHeight
near(近面):定义从距离相机多近的地方开始渲染场景。推荐默认值0.1
far(远面):定义相机可以从它所处的位置看多远。默认值1000
*/
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
//定义相机的位置,有如下两种方式。如果不设置的话,相机位置为默认的Vector3{x:0,y:0,z:0}
//camera.position.set( 0, 200, 800 );
camera.position.y = 200;
camera.position.z = 800;
scene = new THREE.Scene();
// Grid,绘制网格
var size = 500, step = 100;
var geometry = new THREE.Geometry();
for ( var i = - size; i <= size; i += step ) {
//从左下角到右上角,先画横线,在画竖线
//绘制与X轴平行的横线
geometry.vertices.push( new THREE.Vector3( - size, - 120, i ) );
geometry.vertices.push( new THREE.Vector3( size, - 120, i ) );
//绘制与Z轴平行的竖线
geometry.vertices.push( new THREE.Vector3( i, - 120, - size ) );
geometry.vertices.push( new THREE.Vector3( i, - 120, size ) );
}
var material = new THREE.LineBasicMaterial( { color: 0xffffff, opacity: 0.2 } );
/*
THREE.Line使用WebGL中的gl.LINE_STRIP(一系列的连续直线,即折线)渲染
THREE.LineSegments使用WebGL中的gl.LINES(每一对顶点被解释为一条直线,即线段)渲染
*/
var line = new THREE.LineSegments( geometry, material );
scene.add( line );
/*
Sphere,球体
function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {……}
radius: 半径
widthSegments: 水平方向上的分段数,段数越多,球体表面越光滑,默认是8。最小是3
heightSegments: 垂直方向上的分段数,段数越多,球体表面越光滑,默认是6,最小是2
phiStart: 从X轴的什么地方开始绘制(X轴负方向为起始点,逆时针方向为正,即符合右手螺旋定则)
phiLength: 绘制的弧度
thetaStart: 从Y轴的什么地方开始绘制(Y轴正方向为起始点,逆时针方向为正,即符合右手螺旋定则)
thetaLength: 绘制的弧度
*/
var geometry = new THREE.SphereGeometry( 100, 14, 7 );
var textureLoader = new THREE.TextureLoader();
var earthTexture = textureLoader.load( 'textures/land_ocean_ice_cloud_2048.jpg' );
var envMap = textureLoader.load( 'textures/envmap.png' );
/*
exports.UVMapping = UVMapping; 平展-映射
exports.CubeReflectionMapping = CubeReflectionMapping; 立方体反射-映射
exports.CubeRefractionMapping = CubeRefractionMapping; 立方体折射-映射
exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; 矩形球面反射-映射
exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; 矩形球面折射-映射
exports.SphericalReflectionMapping = SphericalReflectionMapping; 球面反射-映射
exports.CubeUVReflectionMapping = CubeUVReflectionMapping; 立方体纹理反射-映射
exports.CubeUVRefractionMapping = CubeUVRefractionMapping; 立方体纹理折射-映射
*/
envMap.mapping = THREE.SphericalReflectionMapping;
var materials = [
/*
MeshBasicMaterial:与光照无关,仅根据材质的颜色或贴图来渲染物体
color:材质的颜色
map:材质的贴图
wireframe: 显示三角形线框还是显示面
side:可选的值有THREE.FrontSide(仅渲染正面)、THREE.BackSide(仅渲染背面)、THREE.DoubleSide(双面渲染)
overdraw: 过渡描绘。如果用THREE.CanvasRenderer对象,有缝隙时需设置该值。例如当前如果使用0.5以下的值,三角形的分界线就很明显。但是使用WebGLRenderer则不会有分割线
visible: 对象是否可见
*/
new THREE.MeshBasicMaterial( { color: 0x00ffff, wireframe: true, side: THREE.DoubleSide } ),//青色
/*
var BlendingMode = {
NoBlending: NoBlending,
NormalBlending: NormalBlending,
AdditiveBlending: AdditiveBlending,
//是一种加法混合模式,材质中的黑色会被当做是透明的
SubtractiveBlending: SubtractiveBlending,
MultiplyBlending: MultiplyBlending,
CustomBlending: CustomBlending
};
*/
new THREE.MeshBasicMaterial( { color: 0xff0000, blending: THREE.AdditiveBlending } ),//红色
/*
MeshLambertMaterial,这种材质会考虑光照的影响,可以用来创建颜色暗淡的、不光亮的物体
color: 即diffuse,漫射颜色,默认为0xffffff,白色
ambient: 环境色,默认为0xffffff, 白色, 乘以环境光得到对象的颜色
emissive: 自发光(荧光)颜色,默认为0x000000,黑色,实体颜色,不受其他灯光的影响.
overdraw: 过渡描绘。如果用THREE.CanvasRenderer对象,有缝隙时需设置该值。例如当前如果使用0.5以下的值,三角形的分界线就很明显。但是使用WebGLRenderer则不会有分割线
morphTargets: 表示是否启用变形
*/
new THREE.MeshLambertMaterial( { color: 0xffffff, overdraw: 0.5 } ),//白色
new THREE.MeshLambertMaterial( { color: 0xffffff, overdraw: 0.5 } ),//白色
//MeshNormalMaterial,这是一种简单的材质,根据物体表面的法向量计算颜色
new THREE.MeshNormalMaterial( { overdraw: 0.5 } ),
new THREE.MeshBasicMaterial( { map: earthTexture } ),
/*
环境贴图(Environment Mapping,EM)也称为反射贴图(Reflection Mapping)
把反射对象当作一个虚拟眼睛,生成一张虚拟的纹理图,然后把该纹理图映射到反射对象上,得到的图像就是该场景的一个影像。
即使反射对象旋转,反射贴图也不会像普通map一样一起旋转。
*/
new THREE.MeshBasicMaterial( { envMap: envMap, overdraw: 0.5 } )
];
for ( var i = 0, l = geometry.faces.length; i < l; i ++ ) {
/*
face {
a, b, c, 组成面的三个顶点索引
color, 面的颜色
materialIndex, MultiMaterial特有,标识面采用的子材质索引
normal{x,y,z}, 面正方向的法向量
vertexColors: Array,
vertexNormals: Array[3],
}
*/
var face = geometry.faces[ i ];
console.dir(face);
if ( Math.random() > 0.5 ) face.materialIndex = Math.floor( Math.random() * materials.length );
}
materials.push( new THREE.MultiMaterial( materials ) );
objects = [];
for ( var i = 0, l = materials.length; i < l; i ++ ) {
var sphere = new THREE.Mesh( geometry, materials[ i ] );
sphere.position.x = ( i % 5 ) * 200 - 400;
sphere.position.z = Math.floor( i / 5 ) * 200 - 200;
sphere.rotation.x = Math.random() * 200 - 100;
sphere.rotation.y = Math.random() * 200 - 100;
sphere.rotation.z = Math.random() * 200 - 100;
objects.push( sphere );
scene.add( sphere );
}
var PI2 = Math.PI * 2;
var program = function ( context ) {
context.beginPath();
//填充一个半径为0.5的圆
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
};
// Lights
/*
环境光,这是一种基础光源,它的颜色会添加到整个场景和所有对象的当前颜色上 |
AmbientLight( color, intensity)
color 光源的颜色
intensity 光照强度,默认为1
*/
scene.add( new THREE.AmbientLight( Math.random() * 0x202020 ) );
/*
平行光,也称作无限光,平行光光源就如同太阳,若在场景中添加了一个平行光,它可以影响场景中的所有物体,而无论平行光光源设置在任何位置。平行光的方向为它的位置指向场景中心。
DirectionalLight( color, intensity)
color 光源的颜色
intensity 光照强度,默认为1
*/
var directionalLight = new THREE.DirectionalLight( Math.random() * 0xffffff );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
//normalize(): 单位化向量,使其模长为1
directionalLight.position.normalize();
scene.add( directionalLight );
/*
点光源,空间中的一点,朝所有方向发射光线
PointLight( color, intensity, distance, decay );
color:光的颜色
intensity:光照强度,默认为1
distance:光最长能照射的距离,默认为0
decay:光的衰减系数,默认为1
*/
pointLight = new THREE.PointLight( 0xffffff, 1 );
scene.add( pointLight );
/*
雪碧图材质,也叫精灵图
CSS Sprites其实就是把网页中一些背景图片整合到一张图片文件中,再利用CSS的“background-image”,“background- repeat”,“background-position”的组合进行背景定位,background-position可以用数字精确的定位出背景图片的位置。
在Three.js中意思有点稍微不一样
A sprite is a plane that always faces towards the camera, generally with a partially transparent texture applied.
Sprites do not cast shadows, setting castShadow = true will have no effect.
一个精灵也就是一个平面,它的面总是朝向相机,通常来说会采用一个部分透明的纹理
精灵不能投影出阴影,设置 castShadow = true 没有任何效果
SpriteCanvasMaterial:
Create a material that can draw custom sprites using a 2d canvas.
创建一个使用2d画布绘制的自定义精灵图材质
材质color会在program之前被设置到context.fillStyle上。因此如果内部还有对fillStyle的设置,将会直接覆盖掉外部的颜色配置
*/
var sprite = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0xffffff, program: program } ) );
//sprite默认是边长为1个像素的画布,scale缩放了它的边长
sprite.scale.set( 8, 8, 8 );
//只要是一个THREE.Object,就有add方法。add之后就加到children数组中了,效果和group一样
pointLight.add( sprite );
console.dir(pointLight);
renderer = new THREE.CanvasRenderer();
//设置屏幕像素比,与Android上的DIP相仿,作用是在所有设备上的显示效果都相近
renderer.setPixelRatio( window.devicePixelRatio );
//设置待渲染场景的大小
renderer.setSize( window.innerWidth, window.innerHeight );
//将渲染器的DOM元素(即Canvas)添加到HTML中
container.appendChild( renderer.domElement );
var debugCanvas = document.createElement( 'canvas' );
debugCanvas.width = 512;
debugCanvas.height = 512;
debugCanvas.style.position = 'absolute';
debugCanvas.style.top = '0px';
debugCanvas.style.left = '0px';
container.appendChild( debugCanvas );
debugContext = debugCanvas.getContext( '2d' );
/*
setTransform为缩放(scale)、旋转(rotate)、移动(translate)的复合方法。当您调用 setTransform() 时,它都会重置前一个变换矩阵然后构建新的矩阵。旋转的中点是它的左上角坐标
context.setTransform(a,b,c,d,e,f);
a 水平缩放
b 左上角固定,向下倾斜,垂直方向不变
c 左上角固定,向右倾斜,水平方向不变
d 垂直缩放
e 水平移动
f 垂直移动
这里就相当于把坐标轴移到了画布中心的位置,后面所有的操作都会针对这个新的坐标系
*/
debugContext.setTransform( 1, 0, 0, 1, 256, 256 );
debugContext.strokeStyle = '#000000';
//左上角的统计信息(FPS,渲染时间,chrome内存使用率)
stats = new Stats();
//这里注意,统计插件的dom元素是"dom",而不是domElement
container.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
//重新设置相机的宽高比。如果宽高比不对,那么正方形可能就不是正方形了
camera.aspect = window.innerWidth / window.innerHeight;
//更新透视相机的投影矩阵
camera.updateProjectionMatrix();
//更新待渲染场景的大小
renderer.setSize( window.innerWidth, window.innerHeight );
}
function loadImage( path ) {
var image = document.createElement( 'img' );
/*映射模式
THREE.UVMapping 平展映射,
THREE.CubeReflectionMapping 立方体反射映射,
THREE.CubeRefractionMapping 立方体折射映射,
THREE.SphericalReflectionMapping球面反射映射,
THREE.SphericalRefractionMapping球面折射映射.
*/
var texture = new THREE.Texture( image, THREE.UVMapping );
//由于图片是异步加载的,一旦检测到needsUpdate=true,three.js就会使用_gl.texImage2D将空的纹理数据传输到显存中,然后就将这个标志位设成false, 之后再使用该图片的时候就不需要重新加载显存数据了
image.onload = function () { texture.needsUpdate = true; };
image.src = path;
return texture;
}
function animate() {
requestAnimationFrame( animate );
render();
//这里可以在render前后使用stats.begin和stats.end,也可以在每次渲染的时候调用一次stats.update
stats.update();
}
function render() {
//Date.now()得到的是当前时间戳,单位毫秒
var timer = Date.now() * 0.0001;
camera.position.x = Math.cos( timer ) * 1000;
camera.position.z = Math.sin( timer ) * 1000;
camera.lookAt( scene.position );
for ( var i = 0, l = objects.length; i < l; i++ ) {
var object = objects[ i ];
//分别绕着X、Y轴按右手螺旋定则进行旋转
object.rotation.x += 0.01;
object.rotation.y += 0.005;
}
pointLight.position.x = Math.sin( timer * 7 ) * 300;
pointLight.position.y = Math.cos( timer * 5 ) * 400;
pointLight.position.z = Math.cos( timer * 3 ) * 300;
renderer.render( scene, camera );
}
</script>
</body>
</html>