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


10

업데이트 5

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


업데이트 4

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


업데이트 3

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

  • 암호

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


업데이트 2

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


최신 정보

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

  • TransformUv기하학과 UV 변환을 처리하는 변환기 방법을 허용합니다. 콜백은 UV를 각 얼굴 어레이 및 대응 수용 Face3geometry.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는 표시등이 없거나 자료가 그림자하지 않는 경우, 일반 질감의. 위치 와 같은 속성 및 유니폼 은 GeometryBufferGeometry에 의해 제공 되므로 다른 위치에서 검색 할 필요가 없습니다. : 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.