threejs:PlaneGeometry将纹理加载到特定面

时间:2018-01-22 15:41:44

标签: javascript three.js

是否可以将纹理加载到PlaneGeometry对象的特定面?我想要做的是将单独的纹理映射到PlaneGeometry对象的特定2个三角形。

以下是一些示例,为PlaneGeometry个对象的每个2个三角形着色并使用某种颜色,并且它可以正常工作:

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>title</title>
		<meta charset="utf-8">
		<style>
			body {
				font-family: Monospace;
				background-color: #f0f0f0;
				margin: 0px;
				overflow: hidden;
			}
		</style>
	</head>
	
<body>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
	<script>
		
		var camera, scene, renderer, geometry, material, mesh;

		init();
		animate();
		
		function init() {

			scene = new THREE.Scene();

			camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
			camera.position.z = 500;
			scene.add(camera);

			// geometry
			var thumbnail_width = 32;
			var thumbnail_height = 32;
			var width_segments = 10;
			var height_segments = 10;
			var plane_geometry_width = width_segments * thumbnail_width;
			var plane_geometry_height = height_segments * thumbnail_height;
			var geometry = new THREE.PlaneGeometry(plane_geometry_width, plane_geometry_height, width_segments, height_segments); // faces.length = widthSegments*heightSegments*2
			
			//Draw grid with static colors
			// materials
			var materials = [];
			materials.push(new THREE.MeshBasicMaterial({
				color: 0xff0000, side:THREE.DoubleSide
			}));
			materials.push(new THREE.MeshBasicMaterial({
				color: 0x00ff00, side:THREE.DoubleSide
			}));
			materials.push(new THREE.MeshBasicMaterial({
				color: 0x0000ff, side:THREE.DoubleSide
			}));
			// Add materialIndex to face
			var l = geometry.faces.length / 2;
			for (var i = 0; i < l; i++) {
				var j = 2 * i;
				geometry.faces[j].materialIndex = i % 3;
				geometry.faces[j + 1].materialIndex = i % 3;
			}

			// mesh
			mesh = new THREE.Mesh(geometry, materials);
			scene.add(mesh);
			
			// WebGL renderer
			renderer = new THREE.WebGLRenderer();
			renderer.setSize(window.innerWidth, window.innerHeight);	
			document.body.appendChild(renderer.domElement);
		}

		function animate() {
			requestAnimationFrame(animate);
			render();
		}

		function render() {
			renderer.render(scene, camera);
		}
		
	</script>
</body>

结果如下: enter image description here

然而,当我尝试以相同的方式映射纹理时,它会映射到所有三角形,这不是我想要的:

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>title</title>
		<meta charset="utf-8">
		<style>
			body {
				font-family: Monospace;
				background-color: #f0f0f0;
				margin: 0px;
				overflow: hidden;
			}
		</style>
	</head>
	
<body>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
	<script>
		
		var camera, scene, renderer, geometry, material, mesh;

		init();
		animate();
		
		function init() {

			scene = new THREE.Scene();

			camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
			camera.position.z = 500;
			scene.add(camera);

			// geometry
			var thumbnail_width = 32;
			var thumbnail_height = 32;
			var width_segments = 10;
			var height_segments = 10;
			var plane_geometry_width = width_segments * thumbnail_width;
			var plane_geometry_height = height_segments * thumbnail_height;
			var geometry = new THREE.PlaneGeometry(plane_geometry_width, plane_geometry_height, width_segments, height_segments); // faces.length = widthSegments*heightSegments*2
			
			var texture = new THREE.TextureLoader().load("image_small/item_1.png")
			
			var materials = [];
			materials.push(new THREE.MeshBasicMaterial({map : texture, side: THREE.DoubleSide}));
			materials.push(new THREE.MeshBasicMaterial({color: 0xff0000, side:THREE.DoubleSide}));
			materials.push(new THREE.MeshBasicMaterial({color: 0x00ff00, side:THREE.DoubleSide}));
			
			// Add materialIndex to face
			var l = geometry.faces.length / 2;
			for (var i = 0; i < l; i++) {
				var j = 2 * i;
				geometry.faces[j].materialIndex = i % 3;
				geometry.faces[j + 1].materialIndex = i % 3;
			}
			
			// mesh
			mesh = new THREE.Mesh(geometry, materials);
			scene.add(mesh);
			
			// WebGL renderer
			renderer = new THREE.WebGLRenderer();
			renderer.setSize(window.innerWidth, window.innerHeight);
			document.body.appendChild(renderer.domElement);
		}

		function animate() {
			requestAnimationFrame(animate);
			render();
		}

		function render() {
			renderer.render(scene, camera);
		}
		
	</script>
</body>

结果如下: enter image description here

更新

@WestLangley建议的第二个解决方案的部分代码

    var texture = new THREE.TextureLoader().load("image_small/item_1.png")
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 10, 10 );
    var materials = [];
    materials.push(new THREE.MeshBasicMaterial({map : texture, side: THREE.DoubleSide}));
    materials.push(new THREE.MeshBasicMaterial({color: 0xff0000, side:THREE.DoubleSide}));
    materials.push(new THREE.MeshBasicMaterial({color: 0x00ff00, side:THREE.DoubleSide}));
    // Add materialIndex to face
    var l = geometry.faces.length / 2;
    for (var i = 0; i < l; i++) {
        var j = 2 * i;
        geometry.faces[j].materialIndex = i % 3;
        geometry.faces[j + 1].materialIndex = i % 3;
    }
    geometry.sortFacesByMaterialIndex();
    // Mesh
    mesh = new THREE.Mesh(geometry, materials);
    scene.add(mesh);
    // WebGL renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

结果: enter image description here

UPDATE2:

    var texture = new THREE.TextureLoader().load("image_small/item_1.png")
    texture.flipY = true
    var materials = [];
    materials.push(new THREE.MeshBasicMaterial({map : texture, side: THREE.DoubleSide}));
    for (var i = 0; i < geometry.faces.length; i++) {
        geometry.faces[i].materialIndex = 0;
    }
    var uvs = geometry.faceVertexUvs[ 0 ]; // widthSegments*heightSegments*2

    //UV
    // (0,1) (1,1)
    // (0,0) (1,0)
    // For THREE.PlaneGeometry, UV ( 0, 0 ) is located at the bottom left, and ( 1, 1 ) the top right.
    for (var i = 0; i < uvs.length; i++) {
        if(i % 2 == 0)
        {
            // (0,1)[2] x
            // (0,0)[1] (1,0)[3]
            uvs[i][0].x = 0.0;
            uvs[i][0].y = 0.0;
            uvs[i][1].x = 0.0;
            uvs[i][1].y = 1.0;
            uvs[i][2].x = 1.0;
            uvs[i][2].y = 0.0;

        }
        else
        {   
            // (0,1)[1] (1,1)[2]
            //     x    (1,0)[3]
            uvs[i][0].x = 0.0;
            uvs[i][0].y = 1.0;
            uvs[i][1].x = 1.0;
            uvs[i][1].y = 1.0;
            uvs[i][2].x = 1.0;
            uvs[i][2].y = 0.0;

        }
    }

    var mesh = new THREE.Mesh(geometry, materials);
    scene.add(mesh);
    // WebGL renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

结果: enter image description here

1 个答案:

答案 0 :(得分:4)

您有两种选择。

一种是更改几何体的UV,使每个正方形的UV从左下角(0,0)到右上角(1,1)。

另一个解决方案是设置纹理的repeat

texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 10, 10 );

BTW,在控制台中键入renderer.info,您将看到正在生成100个绘制调用。

在设置材质索引后,将以下内容添加到代码中:

geometry.sortFacesByMaterialIndex();

您现在应该只看到3个绘制调用。

three.js r.89