作爲一個大學一直在玩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