【问题标题】:threejs: PlaneGeometry load texture to specific facesthreejs:PlaneGeometry 将纹理加载到特定面
【发布时间】:2018-01-23 20:59:04
【问题描述】:

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

这里有一些例子,用某种颜色为PlaneGeometry 对象的每两个三角形着色,它可以工作:

<!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>

结果如下:

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

<!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>

结果如下:

更新:

@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);

结果:

更新 2:

    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);

结果:

【问题讨论】:

  • 最好生成一个Texture Atlas,并调整您的 UV 以映射到图集中的特定图像。

标签: javascript three.js


【解决方案1】:

你有两个选择。

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

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

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

顺便说一句,在控制台中输入renderer.info,您将看到您正在生成 100 个绘图调用。

设置材料索引后,将以下内容添加到您的代码中:

geometry.sortFacesByMaterialIndex();

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

three.js r.89

【讨论】:

  • 第二个解决方案有效,但实际上我需要在这个网格的不同“单元格”中放置不同的纹理。对于第一个解决方案,您能否举例说明如何为网格的“单元”(2,3)填充 UV?
  • 请帮我一个忙,先试试吧。
  • 我在这里找到了您的示例 stackoverflow.com/a/19892947/1179925howewer 纹理 Y 翻转,由于某种原因 texture.flipY = true 没有帮助,请参阅 Update2。
  • 从您的内联 cmets 中,您似乎没有像 PlaneGeometry 那样布置几何体/uvs。看看threejs.org/examples/misc_uv_tests.html。你有正确的想法。 :)
猜你喜欢
  • 1970-01-01
  • 2017-04-28
  • 2011-02-09
  • 1970-01-01
  • 2020-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多