텍스처가 항상 카메라를 향하게하는 방법 ..?


10

업데이트 5

기대되는 모습을 보여주는 또 다른 바이올린을 만들었습니다. 보이지 않는 skydome과 cubecamera가 추가되고 환경 맵이 사용됩니다. 필자의 경우 이미 언급 한 이유로 이러한 기술을 사용해서는 안됩니다.


업데이트 4

중요 : 텍스처가 메쉬 표면에 올바르게 바인딩되는지 관찰하기 위해 대상 메쉬 뒤에 반사 평면이 있으며, 해결하려는 것과는 아무런 관련이 없습니다.


업데이트 3

예상되는 행동이 아닌 것을 보여주기 위해 새로운 바이올린을 만들었습니다.

  • 암호

어쩌면 나는 내 질문을 바꿔야하지만, 해결하려고하는 것에 대해 정확하게 설명 할 지식이 부족합니다 .. 아마도 .. ?)


업데이트 2

(코드 스 니펫이 적용되면 더 이상 사용되지 않습니다.)


최신 정보

확인 .. 3 가지 방법을 추가했습니다.

  • TransformUv기하학과 UV 변환을 처리하는 변환기 방법을 허용합니다. 콜백은 UV를 각 얼굴 어레이 및 대응 수용 Face3의 geometry.faces[]해당 파라미터로한다.

  • MatcapTransformer matcap 변환을 수행하는 uv 변환 핸들러 콜백입니다.

    과

  • 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 쉐이더 언어를 알고 있습니까? 이 효과를 얻는 유일한 방법은 UV 좌표의 기본 동작을 재정의하는 사용자 지정 셰이더를 작성하는 것입니다.
— Marquizzo

@Marquizzo GLSL의 전문가는 아니지만 WebGLRenderTargetCube와 같은 three.js의 소스 코드를 파헤 쳤습니다. GLSL을 ShaderMaterial로 감싸서 찾을 수 있습니다. 내가 말했듯이, '이것에 관한 지식이 부족하여 현재 마시는 것이 너무 많습니다. three.js는 GLSL을 충분히 잘 싸서 가벼워서 GLSL을 직접 다루지 않고도 라이브러리를 사용하여 이와 같은 것을 달성 할 수 있다고 생각했습니다.
— Ken Kin

2
미안하지만, 이것을 할 수있는 유일한 방법은 텍스처가 항상 쉐이더에 그려지고 텍스처 위치가 계산되는 기본 방식을 변경하려고하기 때문에 GLSL을 사용하는 것입니다. discourse.threejs.org
— Marquizzo

픽셀 쉐이더로 GPU 파이프 라인에서 해결할 수 있음을 확인할 수 있습니다
— Mosè Raguzzini

답변:


7

카메라를 향한 모습은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

또는이 질문 에서와 같이 반대쪽 수정 이 필요한 경우 더 좋습니다 .

여기에 이미지 설명을 입력하십시오

이를 위해서는 OP가 실수로 한 것처럼 간단한 조각 셰이더를 설정해야합니다.

버텍스 쉐이더

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

프래그먼트 셰이더

uniform vec2 size;
uniform sampler2D texture;

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

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 자체를 회전시키지 않기 때문에 찾고있는 (상자에 적용됨) 까다 롭습니다. 영사기의 모양과 영사 피사체의 모양이 중요하기 때문에 영사 기술을 사용하는 데에도 단점이 있습니다.

이해를 돕기 위해 : 현실 세계에서는 상자의 3D 공간에서이 효과를 어디에서 볼 수 있습니까? 제 생각에는 3D 표면의 2D 투영 (비주얼 디자인의 투영 매핑)이 유일합니다.


1
전자의 더 많은 것. three.js를 사용하여 사용 하시겠습니까? GLSL에 익숙하지 않습니다. 그리고 첫 번째 애니메이션의 큐브가 동시에 모든 축을 중심으로 회전하면 어떻게 될지 궁금합니다. three.js를 사용하여 구현을 제공 한 후 내 질문이 해결되는지 확인합니다. 유망 보인다 :)
— Ken Kin

1
안녕하세요, 필요한 셰이더를 재현하는 간단한 데모가 포함 된 코드 펜을 추가했습니다.
— Mosè Raguzzini

제 경우에 효과가 있는지 확인할 시간이 필요합니다.
— Ken Kin

1
그것은 질감이 경우, 모핑을 잃지 않고 항상 카메라에 직면 효과가있을 것입니다 항상 ENV는 표시등이 없거나 자료가 그림자하지 않는 경우, 일반 질감의. 위치 와 같은 속성 및 유니폼 은 Geometry 및 BufferGeometry에 의해 제공 되므로 다른 위치에서 검색 할 필요가 없습니다. : three.js를 문서 그것에 대해 좋은 부분이 threejs.org/docs/#api/en/renderers/webgl/WebGLProgram
— 모스 Raguzzini

1
살펴 보시기 바랍니다 jsfiddle.net/7k9so2fr 개정 한을. 텍스처 바인딩의이 동작이 내가 달성하려는 것이 아니라고 말하고 싶습니다. (..
— Ken Kin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.