作为一个大学一直在玩3D的同学,还是应该多写写关于3D的东西,正好把原来做得着色器整理整理,供大家参考也供自己以后回顾吧~
一般的现实中的物体讲求一个平滑着色,能够使其看起来更加真实,而卡通风格的物体则是让被着色物体显得过渡的不那么好,明暗交界线很明显,这样的风格独树一帜,也运用到了很多卡通风格的游戏当中,我在网上看到的卡通风格着色器基于Unity开发的有很多,这里我来基于threejs实现一下卡通着色器的开发。
首先是three创建场景的代码摆放摄像机,加上方向光,加载物体的位置,鼠标移动事件,render开始渲染。这些都不是主要的,最重要的是着色器材质的创建,这里传了2个uniform的参数一个是方向光的方向,另一个是颜色值,由于这两个参数都是固定的,所以不用在每一帧进行改变,传一次值即可。
function init() { container = document.createElement( 'div' ); document.body.appendChild( container ); camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 ); camera.position.z = 20; // scene scene = new THREE.Scene(); var directionalLight = new THREE.DirectionalLight( 0xffffff ); directionalLight.position.set( 1, 1, 1 ); scene.add( directionalLight ); var material = new THREE.MeshPhongMaterial( {color: 0x0908EF} ); //着色器材质 sm = new THREE.ShaderMaterial( { uniforms: { light: {type: 'v3', value: directionalLight.position}, color: { // 方块的基础色 type: 'v3', // 指定变量类型为三维向量 value: new THREE.Color('#1308EF') } }, vertexShader: document.getElementById( 'fish-vertexShader' ).textContent, fragmentShader: document.getElementById( 'fish-fragmentShader' ).textContent, side: THREE.FrontSide, blending: THREE.AdditiveBlending, transparent: true } ); var geometry = new THREE.SphereBufferGeometry( 5, 32, 32 ); var cube = new THREE.Mesh( geometry, sm ); scene.add( cube ); // renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); container.appendChild( renderer.domElement ); document.addEventListener( 'mousemove', onDocumentMouseMove, false ); // window.addEventListener( 'resize', onWindowResize, false ); }下面是顶点着色器的开发,在顶点着色器中用法向量矩阵乘法向量,并进行规格化从而得出每个顶点法线的方向。这里的normal和normalMatrix都是内建变量,不用传给着色器,当需要用到时直接用就行了,然后用投影矩阵X模型矩阵X点的位置,就得到了该顶点在视口坐标系的位置。(这里不懂的同学可以看看图形学方面的书)。
<script id="fish-vertexShader" type="x-shader/x-vertex"> varying vec3 vNormal; void main() { vNormal = normalize(normalMatrix * normal); gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } </script>顶点着色器还是比较简单的,下面就到了重头戏——片元着色器,该功能的实现都是由片元着色器实现的。先介绍原理:片元着色器的功能就是先将光照强度分级,并且将该等级映射到颜色上,然后根据每个片元受到的光照强度给予该片元一个等级的颜色。这样就会人为的制造出着色不平滑的色块效果。diffuse是光强的意思,这里由于我们使用的是方向光,所以直接用方向光乘法线,得到方向光在法线上的投影,这个投影就是光照强度,然后我们将其分为0.8/0.6/0.4/0.2四个等级,根据等级的不同对这四种区域制造出不同的色块,最终实现卡通的效果。
<script id="fish-fragmentShader" type="x-shader/x-vertex"> uniform vec3 light; varying vec3 vNormal; uniform vec3 color; void main() { float diffuse = dot(normalize(light), vNormal); if (diffuse > 0.8) { diffuse = 1.0; } else if (diffuse > 0.5) { diffuse = 0.6; } else if (diffuse > 0.2) { diffuse = 0.4; } else { diffuse = 0.2; } gl_FragColor = vec4( color* diffuse, 1.0); } </script>下面放效果图(MeshPhongMaterial纹理与卡通纹理):
github地址:https://github.com/StringKun/ThreeJSToonShader