浏览代码

added example for decals

spite 11 年之前
父节点
当前提交
2d0d839989

+ 1 - 0
examples/index.html

@@ -113,6 +113,7 @@
 				"webgl_buffergeometry_particles",
 				"webgl_buffergeometry_rawshader",
 				"webgl_buffergeometry_uint",
+				"webgl_decals",
 				"webgl_camera",
 				"webgl_camera_logarithmicdepthbuffer",
 				"webgl_custom_attributes",

+ 280 - 0
examples/js/THREE.DecalGeometry.js

@@ -0,0 +1,280 @@
+//goog.provide( 'green.THREEAddOns' );
+
+THREE.DecalVertex = function( v, n ) {
+
+	this.vertex = v;
+	this.normal = n;
+
+}
+
+THREE.DecalVertex.prototype.clone = function() {
+
+	return new THREE.DecalVertex( this.vertex.clone(), this.normal.clone() );
+
+}
+
+THREE.DecalGeometry = function( mesh, position, rotation, dimensions, check ) {
+
+	THREE.Geometry.call( this );
+
+	if( check === undefined ) check = null;
+	check = check || new THREE.Vector3( 1, 1, 1 );
+
+	this.uvs = [];
+
+	this.cube = new THREE.Mesh( new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z ), new THREE.MeshBasicMaterial() );
+	this.cube.rotation.set( rotation.x, rotation.y, rotation.z );
+	this.cube.position.copy( position );
+	this.cube.scale.set( 1, 1, 1 );
+	this.cube.updateMatrix();
+
+    this.iCubeMatrix = ( new THREE.Matrix4() ).getInverse( this.cube.matrix );
+    
+    this.faceIndices = [ 'a', 'b', 'c', 'd' ];
+
+    this.clipFace = function( inVertices, plane ) {
+
+    	var size = .5 * Math.abs( ( dimensions.clone() ).dot( plane ) );
+
+	    function clip( v0, v1, p ) {
+
+	    	var d0 = v0.vertex.dot( p ) - size,
+				d1 = v1.vertex.dot( p ) - size;
+
+			var s = d0 / ( d0 - d1 );
+			var v = new THREE.DecalVertex( 
+				new THREE.Vector3( 
+					v0.vertex.x + s * ( v1.vertex.x - v0.vertex.x ),
+					v0.vertex.y + s * ( v1.vertex.y - v0.vertex.y ),
+					v0.vertex.z + s * ( v1.vertex.z - v0.vertex.z ) 
+				),
+				new THREE.Vector3(
+					v0.normal.x + s * ( v1.normal.x - v0.normal.x ),
+					v0.normal.y + s * ( v1.normal.y - v0.normal.y ),
+					v0.normal.z + s * ( v1.normal.z - v0.normal.z ) 
+				)
+			);
+
+			// need to clip more values (texture coordinates)? do it this way:
+			//intersectpoint.value = a.value + s*(b.value-a.value);
+
+			return v;
+
+	    }
+
+    	if( inVertices.length === 0 ) return [];
+    	var outVertices = [];
+
+    	for( var j = 0; j < inVertices.length; j += 3 ) {
+
+    		var v1Out, v2Out, v3Out, total = 0;
+
+			var d1 = inVertices[ j + 0 ].vertex.dot( plane ) - size,
+				d2 = inVertices[ j + 1 ].vertex.dot( plane ) - size,
+				d3 = inVertices[ j + 2 ].vertex.dot( plane ) - size;
+
+			v1Out = d1 > 0;
+			v2Out = d2 > 0;
+			v3Out = d3 > 0;
+
+	    	total = ( v1Out?1:0 ) + ( v2Out?1:0 ) + ( v3Out?1:0 );
+
+	    	switch( total ) {
+	    		case 0:{
+					outVertices.push( inVertices[ j ] );
+					outVertices.push( inVertices[ j + 1 ] );
+					outVertices.push( inVertices[ j + 2 ] );
+		    		break;
+		    	}
+	    		case 1:{
+	    			var nV1, nV2, nV3;
+					if( v1Out ) {
+						nV1 = inVertices[ j + 1 ]; 
+						nV2 = inVertices[ j + 2 ];
+						nV3 = clip( inVertices[ j ], nV1, plane );
+						nV4 = clip( inVertices[ j ], nV2, plane );
+					}
+		    		if( v2Out ) { 
+						nV1 = inVertices[ j ]; 
+						nV2 = inVertices[ j + 2 ];
+						nV3 = clip( inVertices[ j + 1 ], nV1, plane );
+						nV4 = clip( inVertices[ j + 1 ], nV2, plane );
+						
+						outVertices.push( nV3 );
+						outVertices.push( nV2.clone() );
+						outVertices.push( nV1.clone() );
+
+						outVertices.push( nV2.clone() );
+						outVertices.push( nV3.clone() );
+						outVertices.push( nV4 );
+						break;
+					}
+		    		if( v3Out ) { 
+						nV1 = inVertices[ j ]; 
+						nV2 = inVertices[ j + 1 ];
+						nV3 = clip( inVertices[ j + 2 ], nV1, plane );
+						nV4 = clip( inVertices[ j + 2 ], nV2, plane );
+					}
+
+					outVertices.push( nV1.clone() );
+					outVertices.push( nV2.clone() );
+					outVertices.push( nV3 );
+
+					outVertices.push( nV4 );
+					outVertices.push( nV3.clone() );
+					outVertices.push( nV2.clone() );
+
+		    		break;
+		    	}
+		    	case 2: {
+		    		var nV1, nV2, nV3;
+		    		if( !v1Out ) { 
+		    			nV1 = inVertices[ j ].clone();
+		    			nV2 = clip( nV1, inVertices[ j + 1 ], plane );
+		    			nV3 = clip( nV1, inVertices[ j + 2 ], plane ); 
+						outVertices.push( nV1 );
+						outVertices.push( nV2 );
+						outVertices.push( nV3 );
+		    		}
+		    		if( !v2Out ) { 
+		    			nV1 = inVertices[ j + 1 ].clone();
+		    			nV2 = clip( nV1, inVertices[ j + 2 ], plane );
+		    			nV3 = clip( nV1, inVertices[ j ], plane );
+						outVertices.push( nV1 );
+						outVertices.push( nV2 );
+						outVertices.push( nV3 );
+					}
+		    		if( !v3Out ) {
+		    			nV1 = inVertices[ j + 2 ].clone();
+		    			nV2 = clip( nV1, inVertices[ j ], plane );
+		    			nV3 = clip( nV1, inVertices[ j + 1 ], plane );
+		    			outVertices.push( nV1 );
+		    			outVertices.push( nV2 );
+		    			outVertices.push( nV3 );
+					}
+
+		    		break;
+		    	}
+		    	case 3: {
+		    		break;
+		    	}
+	    	}
+
+	    }
+
+	    return outVertices;
+
+    }
+
+    this.pushVertex = function( vertices, id, n ){
+
+    	var v = mesh.geometry.vertices[ id ].clone();
+        v.applyMatrix4( mesh.matrix );
+        v.applyMatrix4( this.iCubeMatrix );
+        vertices.push( new THREE.DecalVertex( v, n.clone() ) );
+
+    }
+
+    this.computeDecal = function() {
+
+	    var finalVertices = [];
+
+	    for( var i = 0; i < mesh.geometry.faces.length; i++ ) {
+
+	        var f = mesh.geometry.faces[ i ];
+	        var n = ( f instanceof THREE.Face3 ) ? 3 : 4;
+	        var vertices = [];
+
+	        if( n === 3 ) {
+
+	            this.pushVertex( vertices, f[ this.faceIndices[ 0 ] ], f.vertexNormals[ 0 ] );
+	            this.pushVertex( vertices, f[ this.faceIndices[ 1 ] ], f.vertexNormals[ 1 ] );
+	            this.pushVertex( vertices, f[ this.faceIndices[ 2 ] ], f.vertexNormals[ 2 ] );
+
+	        } else {
+
+	            this.pushVertex( vertices, f[ this.faceIndices[ 0 ] ], f.vertexNormals[ 0 ] );
+	            this.pushVertex( vertices, f[ this.faceIndices[ 1 ] ], f.vertexNormals[ 1 ] );
+	            this.pushVertex( vertices, f[ this.faceIndices[ 2 ] ], f.vertexNormals[ 2 ] );
+
+	            this.pushVertex( vertices, f[ this.faceIndices[ 3 ] ], f.vertexNormals[ 3 ] );
+	            this.pushVertex( vertices, f[ this.faceIndices[ 0 ] ], f.vertexNormals[ 0 ] );
+	            this.pushVertex( vertices, f[ this.faceIndices[ 2 ] ], f.vertexNormals[ 2 ] );
+
+	        }
+
+	        if( check.x ) {
+	        	vertices = this.clipFace( vertices, new THREE.Vector3( 1, 0, 0 ) );
+	        	vertices = this.clipFace( vertices, new THREE.Vector3( -1, 0, 0 ) );
+	        }
+	        if( check.y ) {
+		       	vertices = this.clipFace( vertices, new THREE.Vector3( 0, 1, 0 ) );
+		       	vertices = this.clipFace( vertices, new THREE.Vector3( 0, -1, 0 ) );
+		    }
+		    if( check.z ) {
+		        vertices = this.clipFace( vertices, new THREE.Vector3( 0, 0, 1 ) );
+		        vertices = this.clipFace( vertices, new THREE.Vector3( 0, 0, -1 ) );
+		    }
+
+	        for( var j = 0; j < vertices.length; j++ ) {
+
+				var v = vertices[ j ];
+
+				this.uvs.push( new THREE.Vector2(
+					.5 + ( v.vertex.x / dimensions.x ),
+					.5 + ( v.vertex.y / dimensions.y )
+				) );
+
+				vertices[ j ].vertex.applyMatrix4( this.cube.matrix );
+
+	        }
+
+			if( vertices.length === 0 ) continue;
+
+			finalVertices = finalVertices.concat( vertices );
+
+	    }
+
+	    for( var k = 0; k < finalVertices.length; k += 3 ) {
+	        
+	        this.vertices.push(
+	        	finalVertices[ k ].vertex,
+	        	finalVertices[ k + 1 ].vertex,
+	        	finalVertices[ k + 2 ].vertex
+	        );
+
+	        var f = new THREE.Face3( 
+	            k, 
+	            k + 1, 
+	            k + 2
+	        ) 
+	    	f.vertexNormals.push( finalVertices[ k + 0 ].normal );
+	    	f.vertexNormals.push( finalVertices[ k + 1 ].normal );
+	    	f.vertexNormals.push( finalVertices[ k + 2 ].normal );
+	    	
+	        this.faces.push( f );
+	        
+	        this.faceVertexUvs[ 0 ].push( [
+				this.uvs[ k ],
+				this.uvs[ k + 1 ], 
+				this.uvs[ k + 2 ]
+			] );
+	    
+		}
+
+		this.verticesNeedUpdate = true;
+		this.elementsNeedUpdate = true;
+		this.morphTargetsNeedUpdate = true;
+		this.uvsNeedUpdate = true;
+		this.normalsNeedUpdate = true;
+		this.colorsNeedUpdate = true;
+		this.tangentsNeedUpdate = true;
+		this.computeFaceNormals();
+
+	}
+
+	this.computeDecal();
+
+}
+
+THREE.DecalGeometry.prototype = Object.create( THREE.Geometry.prototype );

二进制
examples/textures/decal/decal-diffuse.png


二进制
examples/textures/decal/decal-normal.jpg


+ 367 - 0
examples/webgl_decals.html

@@ -0,0 +1,367 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<title>WebGL decals</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			*{ box-sizing: border-box; margin: 0; padding: 0 }
+			body {
+				color: #888;
+				font-family: tahoma;
+				font-size:13px;
+				background-color: #222;
+				margin: 0px;
+				overflow: hidden;
+				text-shadow: 0 1px 0 rgba( 0,0,0,.5 )
+			}
+			.intro{ position: absolute; left: 10px; top: 10px; line-height: 1.4em }
+			a{ color: inherit }
+			#container canvas{ position: absolute; left: 0; top: 0; width: 100%; height: 100%; bottom: 0;}
+			b{ color: #aaa;}
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div class="intro">
+			<p><b>Decal Splatter</b> | Click or tap and drag to rotate, mouse wheel or pinch to zoom, click or tap to shoot paint.</p>
+		</div>
+
+<script src="../build/three.min.js"></script>
+<script src="js/controls/OrbitControls.js"></script>
+<script src="js/THREE.DecalGeometry.js"></script>
+<script src="js/libs/dat.gui.min.js"></script>
+
+<script>
+
+var container = document.getElementById( 'container' );
+
+var renderer, scene, helperScene, camera, fov = 45;
+var mesh, decal;
+var projector, raycaster;
+var line;
+var spotLight, pointLight, ambientLight;
+
+var intersection = {
+	intersects: false,
+	point: new THREE.Vector3(),
+	normal: new THREE.Vector3()
+};
+var controls, renderHelpers = false;
+var mouseVector = new THREE.Vector3();
+var mouse = new THREE.Vector2();
+
+var decalMaterial = new THREE.MeshPhongMaterial( { 
+	specular: 0xffffff,
+	shininess: 10,
+	map: THREE.ImageUtils.loadTexture( 'textures/decal/decal-diffuse.png' ), 
+	normalMap: THREE.ImageUtils.loadTexture( 'textures/decal/decal-normal.jpg' ),
+	normalScale: new THREE.Vector2( .15, .15 ),
+	shininess: 30,
+	transparent: true, 
+	depthTest: true, 
+	depthWrite: false, 
+	polygonOffset: true,
+	polygonOffsetFactor: -4, 
+	wireframe: false 
+});
+/*decalMaterial = new THREE.MeshNormalMaterial( { 
+	transparent: true, 
+	depthTest: true, 
+	depthWrite: false, 
+	polygonOffset: true,
+	polygonOffsetFactor: -4, 
+	shading: THREE.SmoothShading
+});*/
+var decals = [];
+var decalHelper, mouseHelper;
+var p = new THREE.Vector3( 0, 0, 0 );
+var r = new THREE.Vector3( 0, 0, 0 );
+var s = new THREE.Vector3( 10, 10, 10 );
+var up = new THREE.Vector3( 0, 1, 0 );
+var check = new THREE.Vector3( 1, 1, 1 );
+
+var params = {
+	projection: 'normal',
+	minScale: 10,
+	maxScale: 20,
+	rotate: true,
+	clear: function() {
+		removeDecals();
+	}
+};
+
+window.addEventListener( 'load', init );
+
+function init() {
+
+	renderer = new THREE.WebGLRenderer( { antialias: true });
+	renderer.setSize( window.innerWidth, window.innerHeight );	
+
+	container.appendChild( renderer.domElement );
+	
+	scene = new THREE.Scene();
+	helperScene = new THREE.Scene();
+
+	camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1000 );
+	camera.position.z = 100;
+	camera.target = new THREE.Vector3();
+	controls = new THREE.OrbitControls( camera, renderer.domElement );
+	
+	scene.add( camera );
+
+	ambientLight = new THREE.AmbientLight( 0x111111 );
+	scene.add( ambientLight );
+
+	pointLight = new THREE.PointLight( 0xff0000 );
+	pointLight.position.z = 10000;
+	pointLight.distance = 4000;
+	scene.add( pointLight );
+
+	pointLight2 = new THREE.PointLight( 0xff5500 );
+	pointLight2.position.z = 1000;
+	pointLight2.distance = 2000;
+	scene.add( pointLight2 );
+
+	pointLight3 = new THREE.PointLight( 0x0000ff );
+	pointLight3.position.x = -1000;
+	pointLight3.position.z = 1000;
+	pointLight3.distance = 2000;
+	scene.add( pointLight3 );
+
+	spotLight = new THREE.SpotLight( 0xaaaaaa );
+	spotLight.position.set( 100, 50, 100 );
+	scene.add( spotLight );
+
+	line = new THREE.Line( new THREE.Geometry( ), new THREE.LineBasicMaterial( { linewidth: 4 }) );
+	line.geometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+	line.geometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+	scene.add( line );
+
+	loadLeePerrySmith();
+
+	projector = new THREE.Projector();
+	raycaster = new THREE.Raycaster();
+
+	mouseHelper = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 10 ), new THREE.MeshNormalMaterial() );
+	scene.add( mouseHelper );
+	mouseHelper.visible = false;
+
+	window.addEventListener( 'resize', onWindowResize, false );
+
+	var moved = false;
+
+	controls.addEventListener( 'change', function() {
+		moved = true;
+	} );
+
+	controls.addEventListener( 'start', function() {
+		moved = false;
+	} );
+
+	controls.addEventListener( 'end', function() {
+		checkIntersection();
+		if( !moved ) shoot();
+	} );
+
+    window.addEventListener( 'mousemove', onTouchMove );
+    window.addEventListener( 'touchmove', onTouchMove );
+
+    function onTouchMove( event ) {
+
+        if( event.changedTouches ) {
+            x = event.changedTouches[ 0 ].pageX;
+            y = event.changedTouches[ 0 ].pageY;
+        } else {
+            x = event.clientX;
+            y = event.clientY;
+        }
+		
+		mouse.x = ( x / window.innerWidth ) * 2 - 1;
+		mouse.y = - ( y / window.innerHeight ) * 2 + 1;
+
+		checkIntersection();
+
+    }
+
+    function checkIntersection() {
+
+    	if( !mesh ) return;
+    	
+    	mouseVector.set( mouse.x, mouse.y, 1 );
+		projector.unprojectVector( mouseVector, camera );
+
+		raycaster.set( camera.position, mouseVector.sub( camera.position ).normalize() );
+
+		var intersects = raycaster.intersectObjects( [ mesh ] );
+
+		if ( intersects.length > 0 ) {
+
+			var p = intersects[ 0 ].point;
+			mouseHelper.position.copy( p );
+			intersection.point.copy( p );
+			var n = intersects[ 0 ].face.normal.clone();
+			n.multiplyScalar( 10 );
+			n.add( intersects[ 0 ].point );
+			intersection.normal.copy( intersects[ 0 ].face.normal );
+			mouseHelper.lookAt( n );
+
+			line.geometry.vertices[ 0 ].copy( intersection.point );
+			line.geometry.vertices[ 1 ].copy( n );
+			line.geometry.verticesNeedUpdate = true;
+
+			intersection.intersects = true;
+
+		} else {
+
+			intersection.intersects = false;
+
+		}
+
+    }
+	
+	var gui = new dat.GUI();
+
+	gui.add( params, 'projection', { 'From cam to mesh': 'camera', 'Normal to mesh': 'normal' } );
+	gui.add( params, 'minScale', 1, 30 );
+	gui.add( params, 'maxScale', 1, 30 );
+	gui.add( params, 'rotate' );
+	gui.add( params, 'clear' );
+	gui.open();
+
+	onWindowResize();
+	render();
+	
+}
+
+function loadLeePerrySmith( callback ) {
+
+	var loader = new THREE.JSONLoader();
+
+    loader.load( 'obj/leeperrysmith/LeePerrySmith.js', function( geometry ) {
+
+    	geometry.verticesNeedUpdate = true;
+		geometry.elementsNeedUpdate = true;
+		geometry.morphTargetsNeedUpdate = true;
+		geometry.uvsNeedUpdate = true;
+		geometry.normalsNeedUpdate = true;
+		geometry.colorsNeedUpdate = true;
+		geometry.tangentsNeedUpdate = true;
+
+		var material = new THREE.MeshPhongMaterial( {
+			map: THREE.ImageUtils.loadTexture( 'obj/leeperrysmith/Map-COL.jpg' ),
+			specularMap: THREE.ImageUtils.loadTexture( 'obj/leeperrysmith/Map-SPEC.jpg' ),
+			normalMap: THREE.ImageUtils.loadTexture( 'obj/leeperrysmith/Infinite-Level_02_Tangent_SmoothUV.jpg' ),
+			shininess: 10
+		} );
+
+		mesh = new THREE.Mesh( geometry, material );
+		scene.add( mesh );
+		mesh.scale.set( 10, 10, 10 );
+
+		//scene.add( new THREE.FaceNormalsHelper( mesh, 1 ) );
+		//scene.add( new THREE.VertexNormalsHelper( mesh, 1 ) );
+
+    } );
+
+}
+
+function shoot() {
+
+	if( params.projection == 'camera' ) {
+
+		var dir = camera.target.clone();
+		dir.sub( camera.position );
+
+		p = intersection.point;
+
+		var m = new THREE.Matrix4();
+		var c = dir.clone();
+		c.negate();
+		c.multiplyScalar( 10 );
+		c.add( p );
+		m.lookAt( p, c, up );
+		m = m.extractRotation( m );
+
+		dummy = new THREE.Object3D();
+		dummy.rotation.setFromRotationMatrix( m );
+		r.set( dummy.rotation.x, dummy.rotation.y, dummy.rotation.z );
+
+	} else {
+
+		p = intersection.point;
+		r.copy( mouseHelper.rotation );
+
+	}
+
+	var scale = params.minScale + Math.random() * ( params.maxScale - params.minScale );
+	s.set( scale, scale, scale );
+
+	if( params.rotate) r.z = Math.random() * 2 * Math.PI;
+
+	var m = new THREE.Mesh( new THREE.DecalGeometry( mesh, p, r, s, check ), decalMaterial );
+	decals.push( m );
+	scene.add( m );
+
+}
+
+function removeDecals() {
+
+	decals.forEach( function( d ) {
+		scene.remove( d );
+		d = null;
+	} );
+	decals = [];
+
+};
+
+function mergeDecals() {
+
+	var merge = {};
+	decals.forEach(function (decal) {
+		
+		var uuid = decal.material.uuid;
+		var d = merge[uuid] = merge[uuid] || {};
+		d.material = d.material || decal.material;
+		d.geometry = d.geometry || new THREE.Geometry();
+		d.geometry.merge(decal.geometry, decal.matrix);
+		
+	});
+
+	removeDecals();
+
+	for (var key in merge) {
+		
+		var d = merge[key];
+		var mesh = new THREE.Mesh(d.geometry, d.material);
+		scene.add(mesh);
+		decals.push(mesh);
+	
+	}
+
+}
+
+function onWindowResize() {
+
+	camera.aspect = window.innerWidth / window.innerHeight;
+	camera.updateProjectionMatrix();
+
+	renderer.setSize( window.innerWidth, window.innerHeight );
+
+}
+
+function render() {
+
+	requestAnimationFrame( render );
+	
+	renderer.autoClear = false;
+	renderer.render( scene, camera );
+	if( renderHelpers ) renderer.render( helperScene, camera );
+
+}
+
+</script>
+
+	</body>
+</html>