其实在3D引擎/库的帮助下,我们做webgl开发的难度已经很大大地降低了,熟悉相关API的话,开发一个简单的3D程序可以说是很轻松的事情。
在我看来,webgl的核心就是着色器(顶点着色器、片元着色器),这两者决定了如何在屏幕上绘制出我们想要的效果。所以,无论你是刚入门的3D开发者(我也是刚刚探索3D领域)还是该领域的资深老鸟,对着色器的了解都是至关重要的。
我们一般的软件开发(例如一些管理系统、商城小程序),诸如这些业务逻辑性非常强的程序,都是依靠CPU的高速运算,而做WebGL(3D开发),我们写的程序代码是要跑在GPU上的,因为对于绘图能力来说,GPU是远远高于CPU的,所以,我们做的就是GPU编程了。
今天,就以一个demo来体会一下,three.js中如何编写自定义的着色器并运用到mesh材质上的。
(首先默认大家都已经学会three.js的一些基本操作,例如将一个three.js内置的面板PlaneGeometry放到场景中)
一、添加plane
function addplane(){ var planeGeometry = new THREE.PlaneGeometry(300,150) var meshMaterial = new THREE.MeshPhongMaterial({ color: 0xfff000 * Math.random() }); var plane = new THREE.Mesh(planeGeometry,meshMaterial); scene.add(plane); }
这个函数将会往场景中添加一个Plane,效果如下:
ok,这就是一个plane了。
二、着色器代码
<!-- 顶点着色器 --> <script id="vertex-shader" type="x-shader/x-vertex">
void main(){ gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } </script>
<!-- 顶点着色器 --> <script id="fragment-shader-3" type="x-shader/x-fragment"> uniform vec2 resolution;
vec2 rand(vec2 pos){ return fract( 0.00005 * (pow(pos+2.0, pos.yx + 1.0) * 22222.0)); }
vec2 rand2(vec2 pos){ return rand(rand(pos)); } float softnoise(vec2 pos, float scale){ vec2 smplpos = pos * scale; float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x; float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x; float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x; float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x; vec2 a = fract(smplpos); return mix( mix(c0, c1, smoothstep(0.0, 1.0, a.x)), mix(c2, c3, smoothstep(0.0, 1.0, a.x)), smoothstep(0.0, 1.0, a.y)
); } void main(void){ vec2 pos = gl_FragCoord.xy / resolution.y; pos.x += time * 0.1; float color = 0.0; float s = 1.0; for(int i = 0; i < 8; i++){ color += softnoise(pos+vec2(i)*0.02, s * 4.0) / s / 2.0; s *= 2.0; } gl_FragColor = vec4(color); } </script>
顶点着色器中,进行了一系列的矩阵变换,将图形的顶点转换为屏幕上的像素点;
模型矩阵: 将顶点从局部坐标系转换到世界坐标系中;
视图矩阵: 将顶点从世界坐标转化到视图坐标系下;
投影矩阵: 将顶点从视图坐标系转换到规范立方体中(即屏幕中);
局部坐标系(模型/物体本身)----->世界坐标系----->视图坐标系----->屏幕。
片元着色器中,因为片元着色器是针对顶点着色器输出的顶点数据 (gl_Position)进行逐点绘制的,所以该着色器就是对每个点赋予一个颜色值,针对上面的片元着色器,他接受外界赋予的变量(uniform类型)。
三、着色器材质
下面我们就用两种着色器来生成一个材质。
//创建ShaderMaterial纹理的函数 function createMaterial(vertexShader, fragmentShader) { var vertShader = document.getElementById(vertexShader).innerHTML; //获取顶点着色器的代码 var fragShader = document.getElementById(fragmentShader).innerHTML; //获取片元着色器的代码 //配置着色器里面的attribute变量的值 var attributes = {}; //配置着色器里面的uniform变量的值 var uniforms = { scale: {type: 'f', value: 10}, resolution: {type: "v2", value: new THREE.Vector2(window.innerWidth, window.innerHeight)} }; var meshMaterial = new THREE.ShaderMaterial({ uniforms: uniforms, defaultAttributeValues : attributes, vertexShader: vertShader, fragmentShader: fragShader, transparent: true }); return meshMaterial; }
四、利用shader材质重新绘制plane
function addplane(){ var planeGeometry = new THREE.PlaneGeometry(300,150) var meshMaterial = createMaterial("vertex-shader", "fragment-shader-3"); var plane = new THREE.Mesh(planeGeometry,meshMaterial); scene.add(plane); }
效果:
五、其他着色器效果示例