这片云加了特效,Duang Duang Duang
首先,看下效果页面,感受一下。点我
是不是有点炫咧,这就是运用WebGL简单实现的效果,下面我们一起来看看代码吧。
Three.js
一个优秀的WebGL开源框架,官网地址https://github.com/mrdoob/three.js。点进去你会发现有好多酷炫的效果,而且基于这个库可以快速地写出3d程序。上面的镇魔曲手游官网的云海效果就是基于three.js实现的。
刚开始接触到Three.js,好多东西都是一头雾水的,然后官网的文档说明又是英文,阅读起来实在困难。在此强烈推荐一个中文基础教程,个人觉得对各种术语以及图形的构造和渲染理解起来相对简单些。英语厉害的同学可以忽略。
渲染的三大要素
在Three.js中,要渲染物体到网页中,我们需要3个组建:场景(scene)、相机(camera)和渲染器(renderer)。有了这三样东西,才能将物体渲染到网页中去。
所以,先初始化这三个东东。
1 2 3 | scene = new THREE.Scene(); //场景,一个物体的容器 camera = new THREE.Camera( 30, w / h, 1, 3000 ); //相机,在场景中取一个合适的景,把它拍下来,可以理解为视角。 renderer = new THREE.WebGLRenderer( { antialias: false } ); //渲染器 |
画云
实现步骤:
- 加载纹理
- 画一个平面
- 为平面赋予纹理坐标
- 将纹理应用于材质
加载纹理
在three.js中,纹理即图片。首先加载一张云的图片,方便后面将此纹理以一定的规则映射到平面上。这里要注意一下,采取相对路径,而且图片资源不能跨域,所以要配置一下fis,不然上线后会报cross-origin的错。
1 | var texture = THREE.ImageUtils.loadTexture( './ignore/cloud10.png' ); |
画一个平面以及云的对应坐标
随机画出几百个无规律重叠的云,形成一片云海的即视感。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var geometry = new THREE.Geometry(); var plane = new THREE.Mesh( new THREE.Plane( 64, 64 ) ); //一个可在在三维3D空间无限延伸的二维平面。 var cloud_num = 400; for ( i = 0; i < cloud_num; i++ ) { plane.position.x = Math.random() * 1000 - 600; plane.position.y = - Math.random() * Math.random() * 200 - 15; plane.position.z = 1; plane.rotation.z = Math.random() * Math.PI; plane.scale.x = plane.scale.y = Math.random() * Math.random() * 1.5 + 0.5; GeometryUtils.merge( geometry, plane ); } |
将纹理应用于材质
这里采取一种特殊的材质。
A ShaderMaterial will only be rendered properly by WebGLRenderer, since the GLSL code in the vertexShader and fragmentShader properties must be compiled and run on the GPU using WebGL.
意思大概是vertexShader和fragmentShader两个属性需要特殊配置一下,具体可以查看官方文档。在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 | < script id = "vs" type = "x-shader/x-vertex" > varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } </ script > < script id = "fs" type = "x-shader/x-fragment" > uniform sampler2D map; uniform vec3 fogColor; uniform float fogNear; uniform float fogFar; varying vec2 vUv; void main() { float depth = gl_FragCoord.z / gl_FragCoord.w; float fogFactor = smoothstep( fogNear, fogFar, depth ); gl_FragColor = texture2D( map, vUv ); gl_FragColor.w *= pow( gl_FragCoord.z, 20.0 ); gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor ); } </ script > |
初始化材质后,将纹理应用于材质,这样就可以看到一片云海了
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 | var fog = new THREE.Fog( 0x4584b4, - 100, 3000 ); //材质 material = new THREE.MeshShaderMaterial( { uniforms: { "map" : { type: "t" , value:2, texture: texture }, "fogColor" : { type: "c" , value: fog.color }, "fogNear" : { type: "f" , value: fog.near }, "fogFar" : { type: "f" , value: fog.far } }, vertexShader: document.getElementById( 'vs' ).textContent, fragmentShader: document.getElementById( 'fs' ).textContent, depthTest: false } ); mesh = new THREE.Mesh( geometry, material ); scene.addObject( mesh ); //加入场景 renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); renderer.render( scene, camera ); //渲染出来 |
让画面动起来
有两种实现方式,要么让相机(camera)改变位置,云一直不动;要么就是云动相机不动。然后requestAnimationFrame循环调用渲染器的render函数一直重绘场景,实现动画效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function animate() { requestAnimationFrame( animate ); customRender(); } function render() { var v = 0.13; var position = ( ( new Date().getTime() - start_time ) * v ) % cloud_num; //相机移动 camera.position.x += ( -40 - camera.target.position.x ) * 0.02; camera.position.y += ( -60 - camera.target.position.y ) * 0.02; camera.position.z = - position + cloud_num; camera.target.position.x = camera.position.x; camera.target.position.y = camera.position.y; camera.target.position.z = camera.position.z - 1000; renderer.render( scene, camera ); } |
总结
第一次接触WebGL,某些地方理解还不是很透彻,希望各位大神好好指导一下。某些地方可能说错了也求多多包容。
手机阅读请扫描下方二维码:
性能优化方面能补充下么。。原来的那个参考很消耗资源,哈哈
1
1
12345678
12345678
12345678
12345678
'//and(select'1'from//pg_sleep(6))>'0
1

1

1

1

1

1

1

1

1
1
1
1
1
1
1
1
1