テクスチャを常にカメラに向ける方法..?


10

アップデート5

別のフィドルを作成して、予想される内容を示します。非表示のスカイドームとキューブカメラが追加され、環境マップが使用されます。私の場合、すでに述べた理由のため、これらの手法はどれも使用すべきではありません。


アップデート4

重要:ターゲットメッシュの後ろに反射面があることに注意してください。これは、テクスチャがメッシュサーフェスに正しくバインドされているかどうかを観察するためのものであり、解決しようとしていることとは関係ありません。


アップデート3

新しいフィドルを作成して、予期された動作ではないものを示します

  • コード

多分私は私の質問を言い換えるべきですが、私が解決しようとしていることについて正確に説明する知識が不足しています、助けてください..(Panoramic-Transform-With-Texture-Looking-At-Direction-Locked-Onto-The-Camera多分 .. ?)


アップデート2

(コードスニペットが適用されたため、非推奨になりました。)


更新

OK .. 3つのメソッドを追加しました:

  • TransformUvジオメトリと、uv変換を処理する変換メソッドを受け入れます。コールバックは、各面と対応するFace3のuvs配列を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のソースコードをいくつか掘り出しました。ShaderMaterialでラップされたGLSLを見つけることができます。私が言ったように、これについての知識が不足していて、現時点では飲むには多すぎるでしょう。私はthree.jsがGLSLを十分にラップし、十分に軽量であることを信じて、GLSLを自分で処理しなくても、ライブラリを使用してこのようなことを達成できると思いました。
ケンキン

2
申し訳ありませんが、テクスチャは常にシェーダーで描画され、テクスチャの位置が計算されるデフォルトの方法を変更しようとしているので、これを実行する唯一の方法はGLSLを使用することです。discourse.threejs.orgで
Marquizzo

私は、ピクセルシェーダによってGPUパイプラインで解けるだ、確認することができます
モーゼ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>
  

実行可能な代替手段:キューブマッピング

ここで、キューブマッピングに関するjsfiddleを変更しましたました。

https://jsfiddle.net/v8efxdo7/

立方体はその下のオブジェクトに顔のテクスチャを投影し、カメラを見ています。

注:ライトと内部オブジェクトは固定位置にあるため、ライトは回転に伴って変化しますが、カメラとプロジェクションキューブは両方ともシーンの中心の周りを回転し。

例を注意深く見ると、このテクニックは完璧ではありませんが、キューブのテクスチャのUVアンラップが十字型であるため、探しているもの(ボックスに適用)はトリッキーです。UV自体を回転させることはできません。プロジェクターオブジェクトの形状と投影対象の形状が重要であるため、効果的であり、投影技術の使用には欠点もあります。

理解を深めるために:現実の世界では、ボックスの3Dスペースでこの効果をどこで確認しますか?私の頭に浮かぶ唯一の例は、3Dサーフェス上の2D投影です(ビジュアルデザインのプロジェクションマッピングのような)。


1
前者の詳細。それには、three.jsを使用してください。私はGLSLに詳しくありません。また、最初に表示したアニメーションの立方体が同時にすべての軸を中心に回転するとどうなるのでしょうか。three.jsを使用して実装を提供した後、私の質問が解決されるかどうか試してみます。有望に見える:)
ケン・キン

1
こんにちは、私は必要なシェーダーを再現する簡単なデモをコードペンに追加しました。
モーゼRaguzzini

私のケースで機能するかどうかを確認するのに少し時間が必要です。
ケンキン

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.