วิธีทำให้พื้นผิวหันหน้าเข้าหากล้องเสมอ .. ?


10

อัปเดต 5

สร้างซออื่นเพื่อแสดงสิ่งที่คาดหวังว่าจะมีลักษณะ มีการเพิ่ม skydome ที่มองไม่เห็นและ cubecamera และแผนที่สิ่งแวดล้อมใช้ ในกรณีของฉันไม่ควรใช้เทคนิคเหล่านี้ด้วยเหตุผลที่กล่าวถึงแล้ว


อัปเดต 4

สำคัญ: โปรดทราบว่ามีระนาบสะท้อนแสงที่ด้านหลังของตาข่ายเป้าหมายซึ่งใช้สำหรับสังเกตว่าพื้นผิวนั้นผูกกับพื้นผิวตาข่ายอย่างถูกต้องหรือไม่ไม่มีอะไรเกี่ยวข้องกับสิ่งที่ฉันกำลังพยายามแก้ไข


อัปเดต 3

สร้างซอใหม่เพื่อแสดงสิ่งที่ไม่ใช่พฤติกรรมที่คาดหวัง

  • รหัส

บางทีฉันควรจะเรียบเรียงคำถามของฉันใหม่ แต่ฉันขาดความรู้ในการอธิบายอย่างถูกต้องเกี่ยวกับสิ่งที่ฉันกำลังพยายามแก้ไขโปรดช่วย .. อาจจะ .. ?)


อัปเดต 2

(เลิกใช้แล้วเนื่องจากมีการใช้ข้อมูลโค้ด)


ปรับปรุง

ตกลง .. ฉันได้เพิ่ม 3 วิธี:

  • TransformUvยอมรับรูปทรงเรขาคณิตและวิธีการแปลงซึ่งจัดการการเปลี่ยนรูปแบบยูวี การเรียกกลับยอมรับอาร์เรย์ Uvs สำหรับแต่ละใบหน้าและที่สอดคล้องกันFace3ของการgeometry.faces[]เป็นพารามิเตอร์

  • MatcapTransformer เป็นการเรียกกลับตัวจัดการ uv-transform เพื่อทำการแปลง matcap

    และ

  • fixTextureWhenRotateAroundZAxis ทำงานเหมือนสิ่งที่มีชื่อ

จนถึงขณะนี้ยังไม่มีfixTexture..วิธีการใดที่สามารถใช้งานร่วมกันfixTextureWhenRotateAroundXAxisได้ทั้งหมด ปัญหายังคงไม่ได้รับการแก้ไขฉันหวังว่าสิ่งที่เพิ่งเพิ่มเข้ามาจะช่วยให้คุณช่วยเหลือฉันได้


ฉันกำลังพยายามทำให้พื้นผิวของตาข่ายต้องเผชิญกับกล้องเปอร์สเปคทีฟอยู่เสมอไม่ว่าตำแหน่งญาติจะเป็นอะไร

สำหรับการสร้างกรณีจริงของฉากของฉันและการโต้ตอบจะค่อนข้างซับซ้อนฉันได้สร้างตัวอย่างเล็กน้อยเพื่อแสดงให้เห็นถึงความตั้งใจของฉัน

  • รหัส
    var MatcapTransformer=function(uvs, face) {
    	for(var i=uvs.length; i-->0;) {
    		uvs[i].x=face.vertexNormals[i].x*0.5+0.5;
    		uvs[i].y=face.vertexNormals[i].y*0.5+0.5;
    	}
    };
    
    var TransformUv=function(geometry, xformer) {
    	// The first argument is also used as an array in the recursive calls 
    	// as there's no method overloading in javascript; and so is the callback. 
    	var a=arguments[0], callback=arguments[1];
    
    	var faceIterator=function(uvFaces, index) {
    		xformer(uvFaces[index], geometry.faces[index]);
    	};
    
    	var layerIterator=function(uvLayers, index) {
    		TransformUv(uvLayers[index], faceIterator);
    	};
    
    	for(var i=a.length; i-->0;) {
    		callback(a, i);
    	}
    
    	if(!(i<0)) {
    		TransformUv(geometry.faceVertexUvs, layerIterator);
    	}
    };
    
    var SetResizeHandler=function(renderer, camera) {
    	var callback=function() {
    		renderer.setSize(window.innerWidth, window.innerHeight);
    		camera.aspect=window.innerWidth/window.innerHeight;
    		camera.updateProjectionMatrix();
    	};
    
    	// bind the resize event
    	window.addEventListener('resize', callback, false);
    
    	// return .stop() the function to stop watching window resize
    	return {
    		stop: function() {
    			window.removeEventListener('resize', callback);
    		}
    	};
    };
    
    (function() {
    	var fov=45;
    	var aspect=window.innerWidth/window.innerHeight;
    	var loader=new THREE.TextureLoader();
    
    	var texture=loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
    	texture.wrapS=THREE.RepeatWrapping;
    	texture.wrapT=THREE.RepeatWrapping;
    	texture.center.set(1/2, 1/2);
    
    	var geometry=new THREE.SphereGeometry(1, 16, 16);
    	var material=new THREE.MeshBasicMaterial({ 'map': texture });
    	var mesh=new THREE.Mesh(geometry, material);
    
    	var geoWireframe=new THREE.WireframeGeometry(geometry);
    	var matWireframe=new THREE.LineBasicMaterial({ 'color': 'red', 'linewidth': 2 });
    	mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
    
    	var camera=new THREE.PerspectiveCamera(fov, aspect);
    	camera.position.setZ(20);
    
    	var scene=new THREE.Scene();
    	scene.add(mesh);
      
    	{
    		var mirror=new THREE.CubeCamera(.1, 2000, 4096);
    		var geoPlane=new THREE.PlaneGeometry(16, 16);
    		var matPlane=new THREE.MeshBasicMaterial({
    			'envMap': mirror.renderTarget.texture
    		});
    
    		var plane=new THREE.Mesh(geoPlane, matPlane);
    		plane.add(mirror);
    		plane.position.setZ(-4);
    		plane.lookAt(mesh.position);
    		scene.add(plane);
    	}
    
    	var renderer=new THREE.WebGLRenderer();
    
    	var container=document.getElementById('container1');
    	container.appendChild(renderer.domElement);
    
    	SetResizeHandler(renderer, camera);
    	renderer.setSize(window.innerWidth, window.innerHeight);
    
    	var fixTextureWhenRotateAroundYAxis=function() {
    		mesh.rotation.y+=0.01;
    		texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
    	};
    
    	var fixTextureWhenRotateAroundZAxis=function() {
    		mesh.rotation.z+=0.01;
    		texture.rotation=-mesh.rotation.z
    		TransformUv(geometry, MatcapTransformer);
    	};
    
    	// This is wrong
    	var fixTextureWhenRotateAroundAllAxis=function() {
    		mesh.rotation.y+=0.01;
    		mesh.rotation.x+=0.01;
    		mesh.rotation.z+=0.01;
    
    		// Dun know how to do it correctly .. 
    		texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
    	};
      
    	var controls=new THREE.TrackballControls(camera, container);
    
    	renderer.setAnimationLoop(function() {
    		fixTextureWhenRotateAroundYAxis();
    
    		// Uncomment the following line and comment out `fixTextureWhenRotateAroundYAxis` to see the demo
    		// fixTextureWhenRotateAroundZAxis();
    
    		// fixTextureWhenRotateAroundAllAxis();
        
    		// controls.update();
    		plane.visible=false;
    		mirror.update(renderer, scene);
    		plane.visible=true; 
    		renderer.render(scene, camera);
    	});
    })();
    body {
    	background-color: #000;
    	margin: 0px;
    	overflow: hidden;
    }
    <script src="https://threejs.org/build/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
    
    <div id='container1'></div>

โปรดทราบว่าแม้ว่าตาข่ายจะหมุนเองในการสาธิตนี้ความตั้งใจจริงของฉันคือการทำให้กล้องเคลื่อนที่เหมือนกับการโคจรรอบตาข่าย

ฉันได้เพิ่มโครงลวดเพื่อทำให้การเคลื่อนไหวชัดเจนยิ่งขึ้น อย่างที่คุณเห็นฉันใช้fixTextureWhenRotateAroundYAxisในการทำอย่างถูกต้อง แต่เป็นเพียงแกน y mesh.rotation.yในรหัสจริงของฉันจะถูกคำนวณสิ่งที่ต้องการ

var ve=camera.position.clone();
ve.sub(mesh.position);
var rotY=Math.atan2(ve.x, ve.z);
var offsetX=rotY/(2*Math.PI);

อย่างไรก็ตามฉันขาดความรู้เกี่ยวกับวิธีการทำfixTextureWhenRotateAroundAllAxisอย่างถูกต้อง มีข้อ จำกัด บางประการในการแก้ไขปัญหานี้:

  • ไม่สามารถใช้ CubeCamera / CubeMap เนื่องจากเครื่องไคลเอ็นต์อาจมีปัญหาประสิทธิภาพการทำงาน

  • อย่าทำให้lookAtกล้องเป็นตาข่ายในขณะที่มีรูปทรงเรขาคณิตในที่สุดไม่เพียง แต่เป็นรูปทรงกลมเท่านั้น เทคนิคเหมือนlookAtและคืนค่า.quaternionในเฟรมจะโอเค

โปรดอย่าเข้าใจฉันผิดที่ฉันกำลังถามปัญหา XY เนื่องจากฉันไม่มีสิทธิ์ที่จะเปิดเผยรหัสกรรมสิทธิ์หรือฉันจะไม่ต้องใช้ความพยายามในการสร้างตัวอย่างเล็ก ๆ น้อย ๆ :)


คุณรู้ภาษา GLSL shader ไหม? วิธีเดียวที่จะบรรลุผลนี้คือการเขียน shader ที่กำหนดเองซึ่งจะแทนที่พฤติกรรมเริ่มต้นของพิกัด UV
Marquizzo

@ Marquizzo ฉันไม่เชี่ยวชาญที่ GLSL แต่ฉันได้ขุดซอร์สโค้ดของ three.js เช่น WebGLRenderTargetCube; ฉันสามารถหา GLSL ที่หุ้มด้วย ShaderMaterial ได้ อย่างที่ฉันบอกไปแล้วว่าขาดความรู้รอบตัวและมันก็มากเกินไปที่จะดื่มในตอนนี้ ฉันเชื่อว่า three.js หุ้ม GLSL ดีพอและเบาพอที่ฉันคิดว่าเราสามารถทำให้สิ่งต่าง ๆ เช่นนี้โดยใช้ห้องสมุดโดยไม่ต้องจัดการกับ GLSL
Ken Kin

2
ขออภัยวิธีเดียวที่ฉันสามารถทำได้คือผ่าน GLSL เนื่องจากพื้นผิวนั้นถูกวาดใน shader เสมอและคุณกำลังพยายามเปลี่ยนวิธีการเริ่มต้นวิธีคำนวณตำแหน่งพื้นผิว คุณอาจโชคดีกว่าที่จะถามคำถามประเภท "วิธีการ" ที่discourse.threejs.org
Marquizzo

ฉันสามารถยืนยันได้ว่ามันสามารถแก้ไขได้ใน GPU ไปป์โดย shader พิกเซล
Mosè Raguzzini

คำตอบ:


7

หันหน้าไปทางกล้องจะมีลักษณะดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

หรือดียิ่งขึ้นเช่นเดียวกับในคำถามนี้เมื่อถามถึงการแก้ไขตรงข้าม:

ป้อนคำอธิบายรูปภาพที่นี่

เพื่อให้บรรลุผลนั้นคุณต้องตั้งค่า shader แฟรกเมนต์อย่างง่าย (ตามที่ OP ทำโดยบังเอิญ):

Vertex shader

void main() {
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

ชิ้นส่วน shader

uniform vec2 size;
uniform sampler2D texture;

void main() {
  gl_FragColor = texture2D(texture, gl_FragCoord.xy / size.xy);
}

จำลองการทำงานของ shader กับ Three.js

function main() {
  // Uniform texture setting
  const uniforms = {
    texture1: { type: "t", value: new THREE.TextureLoader().load( "https://threejsfundamentals.org/threejs/resources/images/wall.jpg" ) }
  };
  // Material by shader
   const myMaterial = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: document.getElementById('vertexShader').textContent,
        fragmentShader: document.getElementById('fragmentShader').textContent
      });
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 2;

  const scene = new THREE.Scene();

  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

  const cubes = [];  // just an array we can use to rotate the cubes
  
  const cube = new THREE.Mesh(geometry, myMaterial);
  scene.add(cube);
  cubes.push(cube);  // add to our list of cubes to rotate

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;
    
    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    cubes.forEach((cube, ndx) => {
      const speed = .2 + ndx * .1;
      const rot = time * speed;
      
      
      cube.rotation.x = rot;
      cube.rotation.y = rot;      
    });
   

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
  void main() {
    gl_Position =   projectionMatrix * 
                    modelViewMatrix * 
                    vec4(position,1.0);
  }
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
  uniform sampler2D texture1;
  const vec2  size = vec2(1024, 512);
  
  void main() {
    gl_FragColor = texture2D(texture1,gl_FragCoord.xy/size.xy); 
  }
</script>
<canvas id="c"></canvas>
  

ทางเลือกที่ทำงานได้: Cube Mapping

ที่นี่ฉันได้ปรับเปลี่ยน jsfiddle เกี่ยวกับการแมปคิวบ์อาจเป็นสิ่งที่คุณกำลังมองหา:

https://jsfiddle.net/v8efxdo7/

คิวบ์ฉายภาพพื้นผิวบนใบหน้าวัตถุและมองกล้อง

หมายเหตุ:ไฟเปลี่ยนไปตามการหมุนเนื่องจากแสงและวัตถุภายในอยู่ในตำแหน่งคงที่ในขณะที่กล้องและคิวบ์ฉายหมุนทั้งสองรอบจุดศูนย์กลางของฉาก

หากคุณดูตัวอย่างอย่างรอบคอบเทคนิคนี้ไม่สมบูรณ์แบบ แต่สิ่งที่คุณกำลังมองหา (ใช้กับกล่อง) นั้นเป็นเรื่องยุ่งยากเพราะ UV การแกะของพื้นผิวของลูกบาศก์เป็นรูปกากบาทการหมุนตัว UV เองจะไม่ มีประสิทธิภาพและการใช้เทคนิคการฉายภาพก็มีข้อเสียเช่นกันเพราะรูปร่างวัตถุของโปรเจคเตอร์และรูปร่างของวัตถุฉาย

เพื่อความเข้าใจที่ดีขึ้น: ในโลกแห่งความเป็นจริงคุณเห็นผลกระทบนี้ในพื้นที่ 3 มิติบนกล่องได้อย่างไร ตัวอย่างเดียวที่อยู่ในใจของฉันคือการฉายภาพ 2 มิติบนพื้นผิว 3 มิติ (เช่นการทำแผนที่การฉายภาพในการออกแบบภาพ)


1
มากขึ้นจากอดีต คุณช่วยกรุณาใช้ three.js เพื่อทำมันได้หรือไม่? ฉันไม่คุ้นเคยกับ GLSL และฉันอยากรู้ว่าจะเกิดอะไรขึ้นถ้าก้อนในนิเมชั่นแรกที่คุณแสดงหมุนรอบ ๆ ทุกแกนในเวลาเดียวกัน? หลังจากที่คุณให้การใช้งานโดยใช้ three.js ฉันจะลองดูว่าคำถามของฉันจะได้รับการแก้ไขหรือไม่ ดูมีแนวโน้ม :)
Ken Kin

1
สวัสดีเพื่อนฉันได้เพิ่ม codepen ด้วยการสาธิตง่าย ๆ ทำซ้ำ shader ที่คุณต้องการ
Mosè Raguzzini

ฉันต้องการเวลาสักครู่เพื่อตรวจสอบว่าเหมาะกับกรณีของฉันหรือไม่
Ken Kin

1
มันจะไม่สูญเสีย morphing หากพื้นผิวหันหน้าเข้าหากล้องเสมอเอฟเฟกต์จะเป็นพื้นผิวธรรมดาเสมอหาก env ไม่มีแสงไฟหรือวัสดุไม่หล่อเงา คุณสมบัติและเครื่องแบบเหมือนตำแหน่งนั้นมีให้โดยGeometryและBufferGeometryดังนั้นคุณไม่จำเป็นต้องเรียกคืนมาจากที่อื่น Three.js docs มีส่วนที่ดีเกี่ยวกับมัน: threejs.org/docs/#api/en/renderers/webgl/WebGLProgram
Mosè Raguzzini

1
โปรดดูที่jsfiddle.net/7k9so2frของฉบับปรับปรุง ฉันพูดได้ว่าพฤติกรรมการผูกมัดพื้นผิวนี้ไม่ใช่สิ่งที่ฉันพยายามจะทำให้สำเร็จ :( ..
Ken Kin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.