Three.js已经简化了很多写shader的工作,最主要的工作就是他把一些常用的矩阵已经作为一个常量提供给我们。
首先来看Three.js官网的说明,一些常用参数已经和几何元素、相机等关联在一起,我们只需要拿来用即可。
来看第一个shader,通过shader改变几何体表面的颜色,效果还是很炫酷的。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Three框架</title> <script src="../library/three.js"></script> <script src="../library/stats.min.js"></script> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 600px; background-color: #EEEEEE; } </style> <script id="fragment_shader" type="x-shader/x-fragment"> uniform float time; varying vec2 vUv; void main( void ) { vec2 position = - 1.0 + 2.0 * vUv; float red = abs( sin( position.x * position.y + time / 5.0 ) ); float green = abs( sin( position.x * position.y + time / 4.0 ) ); float blue = abs( sin( position.x * position.y + time / 3.0 ) ); gl_FragColor = vec4( red, green, blue, 1.0 ); } </script> <script id="vertexShader" type="x-shader/x-vertex"> varying vec2 vUv; void main() { vUv = uv; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; } </script> <script> var renderer; var stats; var clock; var uniforms1; var camera; var scene; var light; var mesh; //初始化webgl function initThree() { width = document.getElementById('canvas-frame').clientWidth; height = document.getElementById('canvas-frame').clientHeight; renderer = new THREE.WebGLRenderer(); renderer.setSize(width, height); document.getElementById('canvas-frame').appendChild(renderer.domElement); renderer.setClearColor(0xFFFFFF, 1.0); clock = new THREE.Clock(); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.top = '0px'; document.getElementById('canvas-frame').appendChild(stats.domElement); } //设置相机 function initCamera() { camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); camera.position.set(0,0,600); camera.up = new THREE.Vector3(0,1,0); camera.lookAt(0,0,0); } //初始化场景 function initScene() { scene = new THREE.Scene(); } //设置化灯光 function initLight() { light = new THREE.AmbientLight(0xFF0000); light.position.set(100, 100, 200); scene.add(light); } //几何物体 function initObject() { uniforms1 = { time: { value: 1.0 } }; var params = uniforms1; var geometry = new THREE.CylinderGeometry(100, 150, 400); var material = new THREE.ShaderMaterial({ uniforms: params, vertexShader: document.getElementById('vertexShader').textContent, fragmentShader: document.getElementById('fragment_shader').textContent }); mesh = new THREE.Mesh(geometry, material); mesh.position = new THREE.Vector3(0, 0, 0); scene.add(mesh); } //运行webgl function threeStart() { initThree(); initCamera(); initScene(); initLight(); initObject(); animation(); } //设置动态场景 function animation() { var delta = clock.getDelta(); uniforms1.time.value += delta * 5; renderer.render(scene, camera); requestAnimationFrame(animation); stats.update(); } </script> </head> <body onload="threeStart();"> <div id="canvas-frame"></div> </body> </html>
然后再来分析代码,看是如何实现的。
1)先看vertexshader,这个简单明了,gl_Position=mvp,然后将UV坐标传递到fragmentshader中,在fragmentshader中定义一个time参数,然后点的颜色随着时间和位置的变化而变化。
2)接下来在创建几何物体的时候,取到time这个参数,几何体中的材料采用shaderMaterial();
3)最后在animation里随着时间改变time这个参数。