Skip to content

Instantly share code, notes, and snippets.

@edom18
Created August 5, 2013 17:56
Show Gist options
  • Save edom18/6157977 to your computer and use it in GitHub Desktop.
Save edom18/6157977 to your computer and use it in GitHub Desktop.
Three.jsでトゥーンシェーダの実装の実験
#Three.jsでトゥーンシェーダの実装の実験をしていきます。
実装については以下の記事を参考にさせてもらいました。
[トゥーンレンダリング | wgld.org](http://wgld.org/d/webgl/w048.html)
##更新履歴
* 2013.08.06 - シェーダ切り替えスイッチを追加。
##現状の問題点
<del>なぜかカリングの設定がうまく動いていない・・。なにか勘違いしているのだろうか・・。
なにか知っている人いたら教えて下さい。
↓どうもこれが想定通りに動いていない。
`renderer.setFaceCulling(THREE.CullFaceFront);`</del>
レンダリングについてはMaterial側で設定するのが正解でした↓
`material.side = THREE.BackSide;//or THREE.FrontSize or THREE.DoubleSide`
これで意図通りにレンダリングされました。
今回のハマったポイントなどは[qiitaに解説記事](http://qiita.com/edo_m18/items/8b84e6318ee49713e40e)を投稿しているので、興味ある人は見てみてください。
@import "compass/reset";
img, canvas {
vertical-align: top;
}
#ctrl {
position: absolute;
left: 0;
top: 0;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r58/three.min.js"></script>
<div id="ctrl">
<p><input type="button" id="lightCameraVisibility" value="ライトカメラのWF表示・非表示" /></p>
<p><input type="button" id="switchMaterial" value="シェーダ切り替え" /></p>
<!-- /#ctrl --></div>
<script type="x-shader/x-vertex" id="vs">
uniform bool edge;
varying vec3 vNormal;
void main(void) {
vec3 pos = position;
if (edge) {
pos += normal * 0.04;
}
vNormal = normal;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fs">
precision mediump float;
uniform vec3 lightDirection;
uniform sampler2D texture;
uniform vec4 edgeColor;
varying vec3 vNormal;
void main(void) {
if (edgeColor.a > 0.0) {
gl_FragColor = edgeColor;
}
else {
float diffuse = clamp(dot(vNormal, lightDirection), 0.0, 1.0);
vec4 smpColor = texture2D(texture, vec2(diffuse, 0.0));
gl_FragColor = smpColor;
}
}
</script>
var container,
camera,
scene,
sceneEdge,
projector,
renderer,
mesh,
meshEdge,
lightCameraVisibility = false,
globalLight;
var sceneCube,
cameraCube,
textureCube,
meshCube;
var shadermaterial,
facematerial;
function initSkybox() {
sceneCube = new THREE.Scene();
cameraCube = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 1, 10000);
cameraCube.position.y = -50;
sceneCube.add(cameraCube);
var urls = [
'/assets/k/z/5/Y/kz5YB.jpg',
'/assets/x/P/8/J/xP8JV.jpg',
'/assets/p/S/A/e/pSAes.jpg',
'/assets/4/v/3/x/4v3xz.jpg',
'/assets/g/a/G/S/gaGSG.jpg',
'/assets/x/z/w/I/xzwIo.jpg'
];
textureCube = THREE.ImageUtils.loadTextureCube(urls);
var shader = THREE.ShaderLib.cube;
shader.uniforms.tCube.value = textureCube;
var material = new THREE.ShaderMaterial({
fragmentShader: shader.fragmentShader,
vertexShader : shader.vertexShader,
uniforms : shader.uniforms,
depthWrite : false,
side : THREE.BackSide
});
meshCube = new THREE.Mesh(new THREE.CubeGeometry(1000, 1000, 1000), material);
sceneCube.add(meshCube);
}
function createScene(geometry, materials) {
shadermaterial = new THREE.ShaderMaterial({
fragmentShader: document.getElementById('fs').innerHTML,
vertexShader : document.getElementById('vs').innerHTML,
attributes: {},
uniforms: {
edgeColor: {
type: 'v4',
value: new THREE.Vector4(0, 0, 0, 0)
},
edge: {
type: 'i',
value: true
},
lightDirection: {
type: 'v3',
value: globalLight.position
},
texture: {
type: 't',
value: THREE.ImageUtils.loadTexture('/assets/2/O/M/q/2OMqF.png')
}
}
});
shadermaterial.morphTargets = true;
if (materials) {
for (var i = 0, l = materials.length; i < l; i++) {
materials[i].morphTargets = true;
}
facematerial = new THREE.MeshFaceMaterial(materials);
}
//mesh = new THREE.SkinnedMesh(geometry, shadermaterial);
mesh = new THREE.SkinnedMesh(geometry, facematerial);
mesh.scale.set(100, 100, 100);
mesh.position.y += 40;
mesh.castShadow = true;
mesh.receiveShadow = true;
meshEdge = mesh.clone();
meshEdge.material = shadermaterial;
scene.add(mesh);
sceneEdge.add(meshEdge);
}
function initGrid() {
// Grid
var gmaterial = new THREE.LineBasicMaterial( { color: 0xcccccc } );
var ggeometry = new THREE.Geometry();
var floor = -0.04, step = 1, size = 14;
for ( var i = 0; i <= size / step * 2; i ++ ) {
ggeometry.vertices.push( new THREE.Vector3( - size, floor, i * step - size ) );
ggeometry.vertices.push( new THREE.Vector3( size, floor, i * step - size ) );
ggeometry.vertices.push( new THREE.Vector3( i * step - size, floor, -size ) );
ggeometry.vertices.push( new THREE.Vector3( i * step - size, floor, size ) );
}
var line = new THREE.Line(ggeometry, gmaterial, THREE.LinePieces);
line.scale.set(100, 100, 100);
scene.add(line);
}
function initGround() {
var initColor = new THREE.Color(0x497f13);
var initTexture = THREE.ImageUtils.generateDataTexture(1, 1, initColor);
var groundMaterial = new THREE.MeshPhongMaterial({color: 0xffffff, specular: 0x111111, map: initTexture});
var groundTexture = THREE.ImageUtils.loadTexture('/assets/h/M/n/q/hMnqT.jpg', undefined, function () { groundMaterial.map = groundTexture; });
groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
groundTexture.repeat.set(25, 25);
groundTexture.anisotropy = 6;
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(20000, 20000), groundMaterial);
mesh.position.y = -150;
mesh.rotation.x = -Math.PI / 2;
mesh.receiveShadow = true;
mesh.castShadow = true;
scene.add(mesh);
}
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.y = 100;
camera.position.z = 10;
camera.target = new THREE.Vector3( 0, 150, 0 );
scene = new THREE.Scene();
sceneEdge = new THREE.Scene();
//
globalLight = new THREE.DirectionalLight( 0xefefff, 2 );
globalLight.position.set(1, 1, 1).normalize();
globalLight.castShadow = true;
globalLight.shadowMapWidth = 2048;
globalLight.shadowMapHeight = 2048;
var d = 1000;
globalLight.shadowCameraLeft = -d;
globalLight.shadowCameraRight = d;
globalLight.shadowCameraTop = d;
globalLight.shadowCameraBottom = -d;
globalLight.shadowCameraNear = 1;
globalLight.shadowCameraFar = 1000;
globalLight.shadowCameraFov = 40;
globalLight.shadowCameraVisible = false;
globalLight.shadowBias = 0.0001;
globalLight.shadowDarkness = 0.5;
scene.add(globalLight);
var light2 = new THREE.DirectionalLight( 0xffefef, 2 );
light2.position.set( -1, -1, -1 ).normalize();
scene.add( light2 );
var loader = new THREE.JSONLoader(true);
loader.load('/assets/l/2/m/t/l2mtS', createScene);
//
initGround();
initSkybox();
//
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.sortObjects = false;
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000, 1);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFShadowMap;
renderer.autoClear = false;
container.appendChild(renderer.domElement);
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
var radius = 800;
var theta = 0;
var duration = 1000;
var keyframes = 30, interpolation = duration / keyframes;
var lastKeyframe = 0, currentKeyframe = 0;
var y = 0;
function render() {
camera.position.x = radius * Math.sin(THREE.Math.degToRad(theta));
camera.position.z = radius * Math.cos(THREE.Math.degToRad(theta));
camera.lookAt(camera.target);
if (mesh) {
y += 0.3;
mesh.position.y = 100 * Math.sin(THREE.Math.degToRad(y));
meshEdge.position.y = 100 * Math.sin(THREE.Math.degToRad(y));
// Alternate morph targets
var time = Date.now() % duration;
var keyframe = Math.floor( time / interpolation );
if ( keyframe != currentKeyframe ) {
mesh.morphTargetInfluences[ lastKeyframe ] = 0;
mesh.morphTargetInfluences[ currentKeyframe ] = 1;
mesh.morphTargetInfluences[ keyframe ] = 0;
lastKeyframe = currentKeyframe;
currentKeyframe = keyframe;
}
mesh.morphTargetInfluences[keyframe] = ( time % interpolation ) / interpolation;
mesh.morphTargetInfluences[lastKeyframe] = 1 - mesh.morphTargetInfluences[ keyframe ];
cameraCube.rotation.copy(camera.rotation);
renderer.clear();
renderer.render(sceneCube, cameraCube);
//render front face.
if (meshFlg) {
meshEdge.material.side = THREE.FrontSide;
mesh.material.uniforms.edgeColor.value = new THREE.Vector4(0, 0, 0, 0);
mesh.material.uniforms.edge.value = false;
}
renderer.render(scene, camera);
//render back face as edge.
if (meshFlg) {
meshEdge.material.side = THREE.BackSide;
meshEdge.material.uniforms.edgeColor.value = new THREE.Vector4(0, 0, 0, 1);
meshEdge.material.uniforms.edge.value = true;
renderer.render(sceneEdge, camera);
}
}
}
//
init();
animate();
//Event handlers.
{
document.addEventListener('mousewheel', function (e) {
e.preventDefault();
theta -= e.wheelDelta / 100;
}, false);
document.addEventListener('DOMMouseScroll', function (e) {
theta -= e.detail / 10;
}, false);
document.getElementById('lightCameraVisibility').addEventListener('click', function () {
globalLight.shadowCameraVisible = (lightCameraVisibility = !lightCameraVisibility);
}, false);
var meshFlg = true;
document.getElementById('switchMaterial').addEventListener('click', function () {
if ((meshFlg = !meshFlg)) {
mesh.material = shadermaterial;
}
else {
mesh.material = facematerial;
}
}, false);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment