Atualização 5
Criou outro violino para mostrar como seria o esperado. Um skydome invisível e uma câmera de cubo são adicionados e o mapa do ambiente é usado; no meu caso, nenhuma dessas técnicas deve ser usada pelos motivos já mencionados.
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 cubeCam = new THREE.CubeCamera(.1, 200, 4096);
cubeCam.renderTarget.texture.wrapS = THREE.RepeatWrapping;
cubeCam.renderTarget.texture.wrapT = THREE.RepeatWrapping;
cubeCam.renderTarget.texture.center.set(1 / 2, 1 / 2);
var geoSky = new THREE.SphereGeometry(2, 16, 16);
var matSky = new THREE.MeshBasicMaterial({
'map': texture,
'side': THREE.BackSide
});
var meshSky = new THREE.Mesh(geoSky, matSky);
meshSky.visible = false;
var geometry = new THREE.IcosahedronGeometry(1, 1);
var material = new THREE.MeshBasicMaterial({
'envMap': cubeCam.renderTarget.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);
scene.add(meshSky);
{
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 controls = new THREE.TrackballControls(camera, container);
var fixTextureWhenRotateAroundAllAxis = function() {
mesh.rotation.y += 0.01;
mesh.rotation.x += 0.01;
mesh.rotation.z += 0.01;
cubeCam.update(renderer, scene);
};
renderer.setAnimationLoop(function() {
// controls.update();
plane.visible = false;
{
meshSky.visible = true;
mesh.visible = false;
fixTextureWhenRotateAroundAllAxis();
mesh.visible = true;
meshSky.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>
Atualização 4
Importante: Observe que existe um plano reflexivo atrás da malha de destino, que é para observar se a textura se liga à superfície da malha corretamente, não tem nada a ver com o que estou tentando resolver.
Atualização 3
Criou um novo violino para mostrar qual NÃO é o comportamento esperado
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);
}
};
};
var getVertexShader=function() {
return `
void main() {
gl_Position=projectionMatrix*modelViewMatrix*vec4(position, 1.0);
}
`;
};
var getFragmentShader=function(size) {
return `
uniform sampler2D texture1;
const vec2 size=vec2(`+size.x+`, `+size.y+`);
void main() {
gl_FragColor=texture2D(texture1, gl_FragCoord.xy/size.xy);
}
`;
};
(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 geometry=new THREE.BoxGeometry(2, 2, 2);
// var material=new THREE.MeshBasicMaterial({ 'map': texture });
var material=new THREE.ShaderMaterial({
'uniforms': { 'texture1': { 'type': 't', 'value': texture } }
, 'vertexShader': getVertexShader()
, 'fragmentShader': getFragmentShader({ 'x': 512, 'y': 256 })
});
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);
};
var fixTextureWhenRotateAroundAllAxis=function() {
mesh.rotation.y+=0.01;
mesh.rotation.x+=0.01;
mesh.rotation.z+=0.01;
};
var controls=new THREE.TrackballControls(camera, container);
renderer.setAnimationLoop(function() {
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>
Talvez eu deva reformular minha pergunta, mas não tenho o conhecimento necessário para descrever com precisão o que estou tentando resolver, por favor ajude .. (Transformação panorâmica com textura olhando para a direção travada na câmera talvez .. ?)
Atualização 2
(Descontinuado quando o snippet de código é aplicado.)
Atualizar
OK .. eu adicionei 3 métodos:
TransformUv
aceita uma geometria e um método de transformador que manipula a transformação uv. O retorno de chamada aceita um array uvs para cada face e o correspondente Face3
de geometry.faces[]
como seus parâmetros.
MatcapTransformer
é o retorno de chamada do manipulador de transformação uv para fazer a transformação matcap.
e
fixTextureWhenRotateAroundZAxis
funciona como o nome dele.
Até agora, nenhum dos fixTexture..
métodos pode funcionar em conjunto, também fixTextureWhenRotateAroundXAxis
não está resolvido. O problema permanece sem solução. Desejo que o que foi adicionado possa ajudar você a me ajudar.
Estou tentando fazer com que a textura de uma malha fique sempre de frente para uma câmera de perspectiva ativa, independentemente das posições relativas.
Para construir um caso real da minha cena e a interação seria bastante complexa, construí um exemplo mínimo para demonstrar minha intenção.
- Código
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>
Observe que, embora a própria malha gire nesta demonstração, minha verdadeira intenção é fazer a câmera se mover como orbitando em torno da malha.
Eu adicionei a estrutura de arame para tornar o movimento mais claro. Como você pode ver, eu costumo fixTextureWhenRotateAroundYAxis
fazer isso corretamente, mas é apenas para o eixo y. O mesh.rotation.y
no meu código real é calculado algo como
var ve=camera.position.clone();
ve.sub(mesh.position);
var rotY=Math.atan2(ve.x, ve.z);
var offsetX=rotY/(2*Math.PI);
No entanto, não tenho conhecimento de como fazer fixTextureWhenRotateAroundAllAxis
corretamente. Existem algumas restrições para resolver isso:
O CubeCamera / CubeMap não pode ser usado, pois as máquinas clientes podem ter problemas de desempenho
Não basta fazer da malha lookAt
a câmera, uma vez que são eventualmente de qualquer tipo de geometria, não apenas as esferas; truques como lookAt
e restaurar .quaternion
em um quadro seria ok.
Por favor, não me entenda mal, estou perguntando um problema XY, pois não tenho o direito de expor código proprietário ou não precisaria pagar um esforço para criar um exemplo mínimo :)
Respostas:
De frente para a câmera, será semelhante a:
Ou, melhor ainda, como nesta pergunta , onde a correção oposta é feita:
Para conseguir isso, você precisa configurar um shader de fragmento simples (como o OP acidentalmente fez):
Sombreador de vértice
Tonalizador de fragmentos
Uma simulação de trabalho do shader com o Three.js
Uma alternativa viável: mapeamento de cubo
Aqui eu modifiquei um jsfiddle sobre mapeamento de cubo , talvez seja o que você está procurando:
https://jsfiddle.net/v8efxdo7/
O cubo projeta sua textura de rosto no objeto subjacente e está olhando para a câmera.
Nota: as luzes mudam com a rotação porque a luz e o objeto interno estão em posição fixa, enquanto câmera e o cubo de projeção giram ambos ao redor do centro da cena.
Se você observar atentamente o exemplo, essa técnica não é perfeita, mas o que você está procurando (aplicado a uma caixa) é complicado, porque o desembrulhamento de UV da textura de um cubo é em forma de cruz, girar o próprio UV não será ser eficaz e o uso de técnicas de projeção também tem suas desvantagens, porque a forma do objeto do projetor e a forma do objeto da projeção são importantes.
Apenas para uma melhor compreensão: no mundo real, onde você vê esse efeito no espaço 3D nas caixas? O único exemplo que me vem à mente é uma projeção 2D em uma superfície 3D (como o mapeamento de projeção no design visual).
fonte