Làm thế nào để tạo họa tiết luôn đối diện với máy ảnh ..?


10

Cập nhật 5

Tạo một fiddle khác để hiển thị những gì được mong đợi sẽ như thế nào. Một skydome vô hình và một cubecamera được thêm vào và bản đồ môi trường được sử dụng; trong trường hợp của tôi, không có kỹ thuật nào trong số này nên được sử dụng vì những lý do đã được đề cập.


Cập nhật 4

Quan trọng: Xin lưu ý rằng có một mặt phẳng phản chiếu ở phía sau lưới mục tiêu để quan sát nếu kết cấu liên kết với bề mặt lưới một cách chính xác, nó không liên quan gì đến những gì tôi đang cố gắng giải quyết.


Cập nhật 3

Tạo một câu đố mới để hiển thị những gì KHÔNG phải là hành vi dự kiến

Có lẽ tôi nên viết lại câu hỏi của mình, nhưng tôi thiếu kiến ​​thức để mô tả chính xác về những gì tôi đang cố gắng giải quyết, xin vui lòng giúp đỡ .. (Panoramic-Transform-With-Texture-looking-At-Direction-Lock-Ono-The-Camera có lẽ .. ?)


Cập nhật 2

(Không dùng nữa vì đoạn mã được áp dụng.)


Cập nhật

OK .. Tôi đã thêm 3 phương thức:

  • TransformUvchấp nhận một hình học, và một phương pháp biến áp xử lý biến đổi uv. Gọi lại chấp nhận một mảng UVs cho từng khuôn mặt và tương ứng Face3của geometry.faces[]như các thông số của nó.

  • MatcapTransformer là trình gọi lại xử lý uv-Transform để thực hiện chuyển đổi matcap.

  • fixTextureWhenRotateAroundZAxis hoạt động như những gì nó được đặt tên.

Cho đến nay, không có fixTexture..phương pháp nào có thể hoạt động hoàn toàn, cũng fixTextureWhenRotateAroundXAxiskhông được tìm ra. Vấn đề vẫn chưa được giải quyết, tôi ước những gì vừa thêm có thể giúp bạn giải quyết.


Tôi đang cố gắng làm cho kết cấu của lưới luôn phải đối mặt với máy ảnh phối cảnh hoạt động, bất kể vị trí tương đối là gì.

Để xây dựng một trường hợp thực tế cho cảnh của tôi và sự tương tác sẽ khá phức tạp, tôi đã xây dựng một ví dụ tối thiểu để thể hiện ý định của mình.

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

Xin lưu ý rằng mặc dù lưới tự quay trong phần trình diễn này, ý định thực sự của tôi là làm cho máy ảnh chuyển động như quay quanh lưới.

Tôi đã thêm khung dây để làm cho chuyển động rõ ràng hơn. Như bạn có thể thấy tôi sử dụng fixTextureWhenRotateAroundYAxisđể làm điều đó một cách chính xác, nhưng nó chỉ dành cho trục y. Các mesh.rotation.ytrong mã thật của tôi được tính như thế

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

Tuy nhiên, tôi thiếu kiến ​​thức về cách làm fixTextureWhenRotateAroundAllAxischính xác. Có một số hạn chế trong việc giải quyết điều này:

  • CubeCamera / CubeMap không thể được sử dụng vì các máy khách có thể có vấn đề về hiệu năng

  • Không chỉ đơn giản là làm cho lưới lookAtmáy ảnh vì cuối cùng chúng là bất kỳ loại hình học nào, không chỉ các hình cầu; Thủ thuật như lookAtvà khôi phục .quaterniontrong một khung sẽ ổn.

Xin đừng hiểu sai rằng tôi đang hỏi một vấn đề XY vì tôi không có quyền tiết lộ mã độc quyền hoặc tôi sẽ không phải trả công sức để xây dựng một ví dụ tối thiểu :)


Bạn có biết ngôn ngữ đổ bóng GLSL không? Cách duy nhất để đạt được hiệu ứng này là viết một shader tùy chỉnh ghi đè hành vi mặc định của tọa độ UV.
Marquizzo

@Marquizzo Tôi không phải là chuyên gia về GLSL, tuy nhiên, tôi đã đào một số mã nguồn của ba.js như WebGLRenderTargetCube; Tôi có thể tìm thấy GLSL được bọc bằng ShaderM vật liệu. Như tôi đã nói, tôi thiếu kiến ​​thức xung quanh vấn đề này và nó sẽ là quá nhiều để uống vào lúc này. Tôi tin rằng ba.js đã bao bọc GLSL đủ tốt và cũng đủ nhẹ để tôi nghĩ rằng chúng ta có thể đạt được những thứ như thế này bằng cách sử dụng thư viện mà không phải tự xử lý GLSL.
Ken Kin

2
Xin lỗi, nhưng cách duy nhất tôi có thể nghĩ để thực hiện điều này là thông qua GLSL, vì kết cấu luôn được vẽ trong trình đổ bóng và bạn đang cố gắng thay đổi cách tính vị trí kết cấu mặc định. Bạn có thể may mắn hơn khi hỏi loại câu hỏi "làm thế nào" này tại diễn
ngôn.threejs.org

Tôi có thể xác nhận, điều đó có thể giải quyết được trong đường ống GPU bằng trình đổ bóng pixel
Mosè Raguzzini

Câu trả lời:


7

Đối diện với máy ảnh sẽ như sau:

nhập mô tả hình ảnh ở đây

Hoặc, thậm chí tốt hơn, như trong câu hỏi này , nơi sửa lỗi ngược lại được hỏi:

nhập mô tả hình ảnh ở đây

Để đạt được điều đó, bạn phải thiết lập một shader mảnh đơn giản (như OP đã vô tình làm):

Vertex shader

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

Mảnh vỡ

uniform vec2 size;
uniform sampler2D texture;

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

Một mock hoạt động của shader với 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>
  

Một thay thế khả thi: Lập bản đồ khối

Ở đây tôi đã sửa đổi một jsfiddle về ánh xạ khối , có thể là những gì bạn đang tìm kiếm:

https://jsfiddle.net/v8efxdo7/

Khối lập phương kết cấu khuôn mặt của nó trên đối tượng bên dưới và nó đang nhìn vào camera.

Lưu ý: đèn thay đổi theo góc quay vì ánh sáng và vật thể bên trong ở vị trí cố định, trong khi máy ảnh và khối chiếu quay cả hai xung quanh tâm của cảnh.

Nếu bạn cẩn thận xem xét ví dụ, kỹ thuật này không hoàn hảo, nhưng bạn đang tìm kiếm gì (áp dụng cho hộp) là khó khăn, bởi vì tia cực tím của kết cấu của hình khối có hình chữ thập, tự xoay UV sẽ không có hiệu quả và sử dụng các kỹ thuật trình chiếu cũng có nhược điểm của nó, bởi vì hình dạng đối tượng máy chiếu và hình dạng đối tượng chiếu có vấn đề.

Chỉ để hiểu rõ hơn: trong thế giới thực, bạn thấy hiệu ứng này trong không gian 3d trên các hộp ở đâu? Ví dụ duy nhất xuất hiện trong tâm trí tôi là hình chiếu 2D trên bề mặt 3D (giống như ánh xạ chiếu trong thiết kế trực quan).


1
Thêm một trong những trước đây. Bạn có vui lòng sử dụng ba.js để làm điều đó không? Tôi không quen thuộc với GLSL. Và tôi tò mò chuyện gì sẽ xảy ra nếu khối lập phương trong hoạt hình đầu tiên bạn hiển thị xoay quanh mọi trục cùng một lúc? Sau khi bạn cung cấp triển khai của mình bằng ba.js, tôi sẽ thử xem câu hỏi của tôi có được giải quyết không. Có vẻ đầy hứa hẹn :)
Ken Kin

1
Xin chào, tôi đã thêm một codepen với một bản demo đơn giản tái tạo shader bạn cần.
Mosè Raguzzini

Tôi cần một chút thời gian để kiểm tra nếu nó hoạt động cho trường hợp của tôi.
Ken Kin

1
Nó không làm mất đi hình thái, nếu kết cấu luôn đối diện với máy ảnh, hiệu ứng sẽ luôn luôn là một kết cấu đơn giản, nếu env không có đèn hoặc vật liệu không tạo bóng. Các thuộc tính và đồng phục như vị trí được cung cấp bởi GeometryBufferGeometry để bạn không phải truy xuất chúng ở những nơi khác. Tài liệu Three.js có một phần hay về nó: Threejs.org/docs/#api/en/renderers/webgl/WebGLProgram
Mosè Raguzzini

1
Xin hãy xem jsfiddle.net/7k9so2fr của bản sửa đổi. Tôi muốn nói rằng hành vi ràng buộc kết cấu này không phải là điều tôi đang cố gắng đạt được :( ..
Ken Kin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.