threejs实现贴花效果

引用:官方源码
在官方给出的源码中采用的模型格式是tfgl格式,查看源码可知新建DecalGeometry对象时,传入的参数为mesh, position, orientation, size,因此对obj文件不可以直接操作,需要遍历obj中的children,在这里采用drc格式(drc为使用Draco压缩后的格式)的模型实现贴花效果。
DecalGeometry源码中传入的参数:

function DecalGeometry( mesh, position, orientation, size ){...}

实现贴花效果,需要借助DecalGeometry.js,因此要其进行引入:

<script src="js/geometries/DecalGeometry.js"></script>

接着新建贴花时需要的参数,raycaster用于拾取鼠标点击的对象,intersection中有intersect、point、normal,intersect为boolean用于判断鼠标点击的位置是否属于模型表面,point与normal用于存储鼠标点击模型时的point与normal。

var mouse=new THREE.Vector2();
var moved;//
var line;//跟随鼠标
var raycaster;//拾取射线
var intersection;//相交线
var decalPosition;//贴花位置
var decalOrientation;//贴花方向
var decalMaterial;//贴花材质
var decals;//贴花数组
var decalSize;//贴花数组
var mouseHelper;

对参数进行初始化,首先在场景中添加line,若物体表面出现线条,则表明当前位置允许进行贴花操作,通过textureLoader加载所需要的贴花纹理:

function initDecalData(){
	let geometry=new THREE.BufferGeometry();
	geometry.setFromPoints([new THREE.Vector3(),new THREE.Vector3()]);
	line=new THREE.Line(geometry,new THREE.LineBasicMaterial({color:0xff0000}));
	scene.add(line);

	decalSize=new THREE.Vector3( 10, 10, 10 );
	decalPosition=new THREE.Vector3();
	decalOrientation=new THREE.Euler();
	decals=[];

	mouseHelper = new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 10 ), new THREE.MeshNormalMaterial() );
	mouseHelper.visible = false;
	scene.add( mouseHelper );//

	let textureLoader=new THREE.TextureLoader();
	let decalDiffuse=textureLoader.load( 'textures/decal/decal-diffuse.png' );//贴花 
	let decalNormal=textureLoader.load( 'textures/decal/decal-normal.jpg' );

	decalMaterial=new THREE.MeshLambertMaterial( {
		pecular: 0x444444,
			map: decalDiffuse,//贴花图
			normalMap: decalNormal,//法向量纹理
			normalScale: new THREE.Vector2( 1, 1 ),
			shininess: 30,
			transparent: true,//透明度
			depthTest: true,
			depthWrite: false,
			polygonOffset: true,
			polygonOffsetFactor: - 4,
			wireframe: false
	} );

	intersection={//相交线
		intersects:false,
		point:new THREE.Vector3(),
		normal:new THREE.Vector3()
	};
	
}

通过拾取射线对选取的贴花的位置进行判断,是否位于模型表面。当位于模型表面时,获取点击模型时的point:

function checkIntersection(){
	
	if(!model) return;
	raycaster=new THREE.Raycaster();//拾取射线
	raycaster.setFromCamera(mouse,camera);
	let intersects=raycaster.intersectObjects([model]);
     //console.log(intersects);
	if(intersects.length>0){//在模型表面
		let modelPoint=intersects[0].point;//获取模型的point
		mouseHelper.position.copy(modelPoint);
		intersection.point.copy(modelPoint);

		let modelNormal=intersects[0].face.normal;//.clone();
		modelNormal.transformDirection(model.matrixWorld);
		modelNormal.multiplyScalar(10);
		modelNormal.add(modelPoint);

		intersection.normal.copy(modelNormal);
		mouseHelper.lookAt(modelNormal);

		let decalPosition=line.geometry.attributes.position;
		decalPosition.setXYZ(0,modelPoint.x,modelPoint.y,modelPoint.z);
		decalPosition.setXYZ(1,modelNormal.x,modelNormal.y,modelNormal.z);
		decalPosition.needsUpdate=true;

		intersection.intersects=true;//在模型表面 则贴花
	}else{
		intersection.intersects=false;
	}
}

将需要进行贴花操作的模型、获取的point即decalPosition等传入DecalGeometry中,进行贴花操作:

function shoot() {//实现贴花

			decalPosition.copy( intersection.point );
			decalOrientation.copy( mouseHelper.rotation );

			// if ( params.rotate ) orientation.z = Math.random() * 2 * Math.PI;//params为GUI组件

			// var scale = params.minScale + Math.random() * ( params.maxScale - params.minScale );
			// size.set( scale, scale, scale );

			var material = decalMaterial.clone();//克隆贴花材质
			material.color.setHex( Math.random() * 0xffffff );

			var m = new THREE.Mesh( new THREE.DecalGeometry( model, decalPosition, decalOrientation, decalSize ), material );
            
			decals.push( m );//放入数组中,为了后续的清除操作
			scene.add( m );//场景中添加贴花
			console.log(decals);
}

事件监听部分,需要将鼠标点击的二维坐标转换为三维坐标:

			window.addEventListener( 'mousedown', function () {
				moved = false;
			}, false );
			window.addEventListener( 'mouseup', function () {//鼠标抬起
				checkIntersection();//判断是否为合理的位置
				if ( ! moved && intersection.intersects ) shoot();//进行贴花
			} );
			window.addEventListener( 'mousemove', onTouchMove );
			window.addEventListener( 'touchmove', onTouchMove );
			function onTouchMove( event ) {//获取三维场景的坐标
				var x, y;
				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;
				//console.log(mouse.x+","+mouse.y);
				checkIntersection();//合理的位置内线条始终跟着鼠标
			}

运行代码后,点击模型中的任意位置,都可以实现贴花效果,实现如下:

贴花前
点击后:
贴花后
完整代码:
HTML部分:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>贴花</title>
	<script type="text/javascript" src="build/three.js"></script>
	<script type="text/javascript" src="js/loaders/DRACOLoader.js"></script>
	<script type="text/javascript" src="js/loaders/OBJLoader.js"></script>
	<script type="text/javascript" src="js/loaders/MTLLoader.js"></script>
	<script type="text/javascript" src="js/geometries/DecalGeometry.js"></script>
	<script type="text/javascript" src="js/controls/OrbitControls.js"></script>
</head>
<body>
	<div id="webgl-out"></div>
   <script type="text/javascript"  src="drcDecals.js"></script>
</body>
</html>

JS部分:

var scene;
var camera;
var renderer;
var ambientLight,spotLight;
var orbitControls;
var model;
var box;

var mouse=new THREE.Vector2();
var moved;//
var line;//跟随鼠标
var raycaster;//拾取射线
var intersection;//相交线
var decalPosition;//贴花位置
var decalOrientation;//贴花方向
var decalMaterial;//贴花材质
var decals;//贴花数组
var decalSize;//贴花数组
var mouseHelper;

init();
animate();

function init(){
	 moved = false;
	initScene();
	initRenderer();
	initCamera();
	addOrbitControls();
	addLight();
	initDecalData();
	addModeldrc({//添加模型 
		drcPath:"obj/OM132326单150dpi_OM132326/",
		drcName:"OM132326单150dpi_OM132326_outside.drc",
		texturePath:"obj/OM132326单150dpi_OM132326/OM132326单150dpi_OM132326-textures/",
		textureName:"outside_tex_3a1887db.png"
	});
	document.getElementById("webgl-out").appendChild(renderer.domElement);
	window.addEventListener("resize",onWindowResize,false);
	

	orbitControls.addEventListener( 'change', function () {		
		moved = true;
	} );
	window.addEventListener( 'mousedown', function () {

				moved = false;

			}, false );

			window.addEventListener( 'mouseup', function () {//鼠标抬起

				checkIntersection();//判断是否为合理的位置
				if ( ! moved && intersection.intersects ) shoot();//进行贴花

			} );

			window.addEventListener( 'mousemove', onTouchMove );
			window.addEventListener( 'touchmove', onTouchMove );

			function onTouchMove( event ) {//获取三维场景的坐标

				var x, y;

				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;

				//console.log(mouse.x+","+mouse.y);

				checkIntersection();//合理位置内线条始终跟着鼠标

			}
}
function initScene(){
	scene=new THREE.Scene();

}
function initRenderer(){
	renderer=new THREE.WebGLRenderer({antialias:true});//抗锯齿
	renderer.setSize(window.innerWidth,window.innerHeight);
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setClearColor(new THREE.Color(0x4f6cca));

}
function initCamera(){
	camera=new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 1, 1000 );
	camera.position.set(0,0,500);
	scene.add(camera);
}
function addOrbitControls(){
	orbitControls=new THREE.OrbitControls(camera,renderer.domElement);
	orbitControls.autoRotate=false;
	orbitControls.zoomSpeed=1.2;
	orbitControls.rotateSpeed=0.5;
	orbitControls.stopAutoRotateWhenTuch=true;
	orbitControls.enableDamping=false;
	orbitControls.minDistance=60;
	orbitControls.maxDistance=900;
	orbitControls.intensity=0.1;//灵敏度
	orbitControls.enablePan=true;//启用右键

}
function addLight(){
	ambientLight=new THREE.AmbientLight( {color:0x0fffff} );
	scene.add(ambientLight);
	spotLight=new THREE.SpotLight({color:0xffffff});
	spotLight.position.set(100,300,1300);
	scene.add(spotLight);
}
function addModeldrc(drcDef){//加载drc文件
	var material;
	var textureLoader=new THREE.TextureLoader();
	var dracoLoader=new THREE.DRACOLoader();
	THREE.DRACOLoader.setDecoderConfig( { type: 'js' } );//js类型
	//dracoLoader.setPath("obj/OM132326单150dpi_OM132326/");
	dracoLoader.setPath(drcDef.drcPath);

	//dracoLoader.load("OM132326单150dpi_OM132326.drc",function(mesh){
	dracoLoader.load(drcDef.drcName,function(mesh){
	    //var texture=new THREE.ImageUtils.loadTexture(texturePath+"outside_tex_3a1887db.png");
	    var texture=new THREE.ImageUtils.loadTexture(drcDef.texturePath+drcDef.textureName);
	    material=new THREE.MeshLambertMaterial();
	    //material.side=THREE.FrontSide;
	    material.side=THREE.BreakSide;
	    material.map=texture;
	    material.needsUpdate=true;
	    model=new THREE.Mesh(mesh,material);
	    setModelPosition(model);
	    scene.add(model);
	},onProgress, onError);
	var onProgress = function ( xhr ) {
		   	      if ( xhr.lengthComputable ) {
		   		     var percentComplete = xhr.loaded / xhr.total * 100;
		   		      console.log( Math.round(percentComplete, 2) + '% downloaded' );
		   	       }
		        };
	var onError = function ( xhr ) { alert("错误了") ;};


}
function setModelPosition(model){
     box=new THREE.Box3().setFromObject(model);
     var scaleNum=1;
     var modelMax=box.max;
	var modelMin=box.min;
	var modelWidth=modelMax.x-modelMin.x;
	var modelHeight=modelMax.y-modelMin.y;
	if(modelWidth>window.innerWidth/10){
        scaleNum=window.innerWidth/(10*modelWidth);
        model.scale.set(scaleNum,scaleNum,scaleNum);//设置模型的缩放大小
	}
	if(modelHeight>window.innerHeight/3){
		if(scaleNum>window.innerHeight/(3*modelHeight)){
			scaleNum=window.innerHeight/(3*modelHeight);
			model.scale.set(scaleNum,scaleNum,scaleNum);//设置模型的缩放大小
		}
	}
	
	console.log(scaleNum);
	box.max.x*=scaleNum;//重置包围盒的大小
	box.max.y*=scaleNum;
	box.max.z*=scaleNum;
	box.min.x*=scaleNum;
	box.min.y*=scaleNum;
	box.min.z*=scaleNum;
	console.log(box);
	console.log(model.position);
	box.getCenter(model.position);
	model.position.multiplyScalar(-1);
}
function initDecalData(){
	let geometry=new THREE.BufferGeometry();
	geometry.setFromPoints([new THREE.Vector3(),new THREE.Vector3()]);
	line=new THREE.Line(geometry,new THREE.LineBasicMaterial({color:0xff0000}));
	scene.add(line);

	decalSize=new THREE.Vector3( 10, 10, 10 );
	decalPosition=new THREE.Vector3();
	decalOrientation=new THREE.Euler();
	decals=[];

	mouseHelper = new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 10 ), new THREE.MeshNormalMaterial() );
	mouseHelper.visible = false;
	scene.add( mouseHelper );//

	let textureLoader=new THREE.TextureLoader();
	let decalDiffuse=textureLoader.load( 'textures/decal/decal-diffuse.png' );//贴花 
	let decalNormal=textureLoader.load( 'textures/decal/decal-normal.jpg' );

	decalMaterial=new THREE.MeshLambertMaterial( {
		pecular: 0x444444,
			map: decalDiffuse,//贴花图
			normalMap: decalNormal,//法向量纹理
			normalScale: new THREE.Vector2( 1, 1 ),
			shininess: 30,
			transparent: true,//透明度
			depthTest: true,
			depthWrite: false,
			polygonOffset: true,
			polygonOffsetFactor: - 4,
			wireframe: false
	} );

	intersection={//相交线
		intersects:false,
		point:new THREE.Vector3(),
		normal:new THREE.Vector3()
	};
	
}
function checkIntersection(){
	
	if(!model) return;
	raycaster=new THREE.Raycaster();//拾取射线
	raycaster.setFromCamera(mouse,camera);
	let intersects=raycaster.intersectObjects([model]);
     //console.log(intersects);
	if(intersects.length>0){//在模型表面
		let modelPoint=intersects[0].point;//获取模型的point
		mouseHelper.position.copy(modelPoint);
		intersection.point.copy(modelPoint);

		let modelNormal=intersects[0].face.normal;//.clone();
		modelNormal.transformDirection(model.matrixWorld);
		modelNormal.multiplyScalar(10);
		modelNormal.add(modelPoint);

		intersection.normal.copy(modelNormal);
		mouseHelper.lookAt(modelNormal);

		let decalPosition=line.geometry.attributes.position;
		decalPosition.setXYZ(0,modelPoint.x,modelPoint.y,modelPoint.z);
		decalPosition.setXYZ(1,modelNormal.x,modelNormal.y,modelNormal.z);
		decalPosition.needsUpdate=true;

		intersection.intersects=true;//在模型表面 则贴花
	}else{
		intersection.intersects=false;
	}
}
function shoot() {//实现贴花

			decalPosition.copy( intersection.point );
			decalOrientation.copy( mouseHelper.rotation );

			// if ( params.rotate ) orientation.z = Math.random() * 2 * Math.PI;//params为GUI组件

			// var scale = params.minScale + Math.random() * ( params.maxScale - params.minScale );
			// size.set( scale, scale, scale );

			var material = decalMaterial.clone();//克隆贴花材质
			material.color.setHex( Math.random() * 0xffffff );

			var m = new THREE.Mesh( new THREE.DecalGeometry( model, decalPosition, decalOrientation, decalSize ), material );
            
			decals.push( m );//放入数组中,为了后续的清除操作
			scene.add( m );//场景中添加贴花
			console.log(decals);
}
function onWindowResize(){
	camera.aspect=window.innerWidth/window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize(window.innerWidth,window.innerHeight);
}
function animate(){
	requestAnimationFrame(animate);
	render();

}
function render(){
	renderer.render(scene,camera);
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值