Як зробити фактуру завжди обличчям до камери ..?


10

Оновлення 5

Створили ще одну загадку, щоб показати, як виглядатиме очікуване. Додано невидимий скайдом та кубекамера та використовується карта навколишнього середовища; в моєму випадку жодна з цих методик не повинна використовуватися з уже згаданих причин.


Оновлення 4

Важливо: Зверніть увагу, що в задній частині цільової сітки є світловідбиваюча площина, яка спостерігає, чи текстура прив’язана до поверхні сітки правильно, вона не має нічого спільного з тим, що я намагаюся вирішити.


Оновлення 3

Створив нову загадку, щоб показати, що НЕ очікувана поведінка

  • Код

Можливо, я повинен перефразувати своє запитання, але мені не вистачає знань, щоб точно описати те, що я намагаюся вирішити, будь ласка, допоможіть. можливо .. ?)


Оновлення 2

(Застаріло, як застосовано фрагмент коду.)


Оновлення

Добре .. Я додав 3 способи:

  • TransformUvприймає геометрію та метод трансформаторів, який обробляє uv-перетворення. Як передзвонити приймає масив UVs для кожної особи і відповідне Face3з , geometry.faces[]як його параметри.

  • MatcapTransformer являє собою зворотний виклик обробника uv-перетворення для перетворення 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це правильно, але це лише для осі у. У 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 Я не є експертом у GLSL, однак я викопав деякий вихідний код з три.js, як WebGLRenderTargetCube; Я можу знайти GLSL, оброблений ShaderMaterial. Як я вже казав, мені не вистачає знань навколо цього, і на даний момент було б занадто багато пити. Я вважаю, що Three.js завернув GLSL досить добре, а також досить легким, що я вважав, що ми можемо досягти таких речей, використовуючи бібліотеку, не маючи стосунків із GLSL.
Кен Кін

2
Вибачте, але єдиний спосіб, що я можу зробити це - це через GLSL, оскільки текстури завжди малюються в шейдері, і ви намагаєтесь змінити стандартний спосіб обчислення положення текстури. Можливо, вам пощастить задати такі запитання "як робити" на дискурсі.threejs.org
Marquizzo

Я можу підтвердити, що це вирішується в конвеєрі GPU піксельним
шейдером

Відповіді:


7

Облицювання камери виглядатиме так:

введіть тут опис зображення

Або ще краще, як у цьому питанні , де задається протилежне виправлення:

введіть тут опис зображення

Для цього вам потрібно встановити простий шейдер для фрагментів (як це випадково зробив ОП):

Вершина шейдер

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/

Куб проектує свою фактуру обличчя на предмет, що лежить нижче, і він дивиться на камеру.

Примітка: світло змінюється при обертанні, оскільки світло та внутрішній об'єкт знаходяться у фіксованому положенні, тоді як камера та проекційний куб обертаються як навколо центру сцени.

Якщо ви уважно подивитесь на приклад, ця методика не є ідеальною, але те, що ви шукаєте (застосовано до коробки), хитро, тому що УФ-розмотування текстури куба має хрестоподібну форму, обертання самого УФ не буде бути ефективним і використання методів проекції має і свої недоліки, оскільки має значення форма об'єкта проектора та форма предмета проекції.

Просто для кращого розуміння: у реальному світі, де ви бачите цей ефект у 3d просторі на коробках? Єдиний приклад, який мені спадає на думку, - це двовимірна проекція на 3D-поверхні (на зразок відображення проекції у візуальному дизайні).


1
Більше колишнього. Будь ласка, використовуйте three.js для цього? Я не знайомий з GLSL. Мені цікаво, що станеться, якщо куб у першій анімації, яку ви показуєте, обертається навколо кожної осі одночасно? Після того як ви надасте свою реалізацію за допомогою three.js, я спробую перевірити, чи вирішується моє питання. Виглядає перспективно :)
Кен Кін

1
Привіт, приятелю, я додав кодек з простою демонстрацією, що відтворює потрібний вам шейдер.
Mosè Raguzzini

Мені потрібен певний час, щоб перевірити, чи працює він для моєї справи.
Кен Кін

1
Це не втрачає морфінгу, якщо текстура завжди стикається з камерою, ефект завжди буде простої текстури, якщо у анвіолі немає світла або матеріал не кидає тіні. Атрибути та уніформи на зразок положення надаються геометрією та BufferGeometry, тому вам не доведеться їх отримувати в інших місцях. Документи Three.js мають приємний розділ про це: threejs.org/docs/#api/en/renderers/webgl/WebGLProgram
Mosè Raguzzini

1
Погляньте на jsfiddle.net/7k9so2fr оновленого. Я б сказав, що така поведінка зв'язування текстур не є тим, чого я намагаюся досягти :( ..
Кен Кін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.