What is the best way to ensure a texture is always facing the camera?

Latest Update:

We have created a new demo to showcase the desired outcome. The demo includes an invisible skydome, a cubecamera, and an environment map. However, it is important to note that these techniques should not be utilized for the reasons previously mentioned.

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) {
  // Handle uv transformations
};

var SetResizeHandler = function(renderer, camera) {
  // Resize handler function
};

// Other functions and setup code
...
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>


Previous Updates:

Just to clarify, the reflective plane in the demo serves a specific purpose to check texture binding and is not directly related to the main issue being addressed.


Update:

We have introduced new methods and functions to assist in solving the problem. Despite our efforts, the issue with getting the textures to align perfectly with camera movement persists. Any assistance in resolving this would be greatly appreciated.


The main goal is to ensure the texture of a mesh always faces the active perspective camera, regardless of their relative positions. We have provided a simplified example to demonstrate this concept while avoiding complex real-world scenarios.

  • Sample Code
    var MatcapTransformer=function(uvs, face) {
    // Handle matcap transformations
    };
    
    var TransformUv=function(geometry, xformer) {
    // The first argument is also used as an array in the recursive calls
    // Callback functions and array transformations
    };
    
    var SetResizeHandler=function(renderer, camera) {
    // Function to handle resizing of elements
    };
    
    // Other functions and setup code
    ...
    
    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>

Our objective is to have the texture align with the camera's movement, especially when orbiting around the mesh. We have added wireframes to enhance visibility during demonstration. Your help in addressing the complex rotation issue is greatly appreciated.

While we have made progress in rotating the texture around the y-axis, we are struggling with achieving the same effect across all axes. Any insights or guidance on this matter would be highly valued.

Answer №1

When facing the camera, the appearance will be as follows:

https://i.sstatic.net/tITkm.gif

For an even more impressive result, check out this question, where the opposite solution is sought:

https://i.sstatic.net/7t3Vr.gif

To achieve this effect, it is necessary to set up a simple fragment shader (similar to what the OP accidentally did):

Vertex shader

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

Fragment shader

uniform vec2 size;
uniform sampler2D texture;

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

A functional example of the shader using 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>
  

An alternative method: Cube Mapping

In this instance, I have made modifications to a jsfiddle demonstrating cube mapping, which might be more aligned with what you are seeking:

https://jsfiddle.net/v8efxdo7/

The cube projects its face texture onto the underlying object and maintains alignment with the camera.

Note: The lighting changes as the object rotates due to the fixed positions of the light and inner object, while the camera and projection cube both rotate around the scene's center.

Upon closer inspection of the example, this technique, though not flawless, addresses the complexities involved in applying this effect to a box. The UV unwrap of a cube's texture is cross-shaped, rotating the UV itself would not suffice, and using projection techniques presents its own challenges, as the shapes of the projector object and projection subject are crucial factors.

For better comprehension: where in real-world 3D space do we observe this effect on boxes? The only instance that comes to mind is a 2D projection onto a 3D surface (similar to projection mapping in visual design).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A guide to using select2.js to activate a selection based on a data attribute

I'm faced with the following html snippet: <div class='multiWrapper'> <select id="multi" class="js-dropdown-from-code"> <optgroup class='def-cursor' label='Code' data-city ...

Unable to access a frame inside an iframe and frameset using JavaScript when both domains are identical

I am attempting to use JavaScript to access an HTML element within a nested frame in an iframe and frameset. The structure of the HTML is as follows: <iframe id="central_iframe" name="central_iframe" (...)> <frameset cols="185, *" border="0" ...

Tips for shortening extra text in a non-wrapping HTML table cell and adding "..." at the end

My HTML template includes text imported from a database field into a <td> tag. The length of the text can range from 3 to 200 characters, and the <td> spans 100% of the screen width. If the text surpasses the width of the screen, I want it to b ...

Creating markers for every value in a table using Google Maps API v3

Looking for some guidance here. I have a table with multiple values that I need to process using a function. Could someone help me with a suitable loop in jQuery or JavaScript that can achieve this? I'm still learning the ropes of these languages. My ...

JavaScript form validation involves using client-side scripting to ensure that

I'm currently working on form validation, but I'm unsure if I'm overcomplicating things. I have a variable that compares elements and replaces an image with a checkmark for success or an X for failure upon successful validation. The function ...

Utilizing JSON parse/stringify for data manipulation

I'm seeking advice on manipulating JSON data using JavaScript, particularly with the stringify/parse methods. In this scenario, I start by creating a JSON string and then use parse to convert it into an object. However, my goal is to efficiently delet ...

Retrieve the attributes associated with a feature layer to display in a Pop-up Template using ArcGIS Javascript

Is there a way to retrieve all attributes (fields) from a feature layer for a PopupTemplate without explicitly listing them in the fieldInfos object when coding in Angular? .ts const template = { title: "{NAME} in {COUNTY}", cont ...

Guide on setting the dropdown's selected index through JavaScript

Is there a way to use javascript to set the selected value of a dropdown? Here is the HTML code I am working with: <select id="strPlan" name="strPlan" class="formInput"> <option value="0">Select a Plan</option> </select> I am ...

Transmitting information to the service array through relentless perseverance

I need assistance finding a solution to my question. Can my friends help me out? What types of requests do I receive: facebook, linkedin, reddit I want to simplify my code and avoid writing lengthy blocks. How can I create a check loop to send the same ...

Using JavaScript to fetch HTML and apply CSS dynamically

Is there a way to retrieve all HTML within a div along with the corresponding CSS? A majority of the CSS is defined in classes within an external stylesheet. document.getElementById("mydiv") Currently, using the above code only fetches the HTML with inli ...

Instructions on utilizing module.exports to export an async function

I am facing an issue with returning the result of an async function to the route I am calling. How can I resolve this successfully? My goal is to export a token from file token_generator.js and display it on route ('/') using Express. The functi ...

Modify the ColVis Appearance in Datatable Using JavaScript

Where can I modify the background color for the 'Hide/Show columns' label in the ColVis.js file? ...

Configuring route for serving static files in an Express server

I'm completely new to working with express, but I am eager to learn and follow the best practices. My goal is to serve files like CSS or index.html from a folder called 'public'. I have seen examples using .use and .get methods as shown belo ...

Integrating PHP code into a React.js application can provide

I am currently integrating react.js into a section of my app and exploring the possibility of embedding some PHP code into react.js. This would allow me to avoid having to completely rewrite the backend that was originally written in PHP. Here's an ex ...

When using AngularJS, the templateUrl feature delays the initialization of the controller, causing issues with dependent code

Recently, I began experimenting with AngularJS and so far, everything seems to be going smoothly except for one small issue. Let's consider a scenario where I have two directives, with one directive relying on the other, like this: angular.module(&ap ...

Refreshing jQuery via Ajax Response

In our JSF2 application, we encounter situations where we need to re-invoke the JavaScript (specifically jQuery for UI styling) when making Ajax calls. However, it seems that the JavaScript functions are not being called upon receiving the Ajax response fr ...

Ordering an Array of JavaScript Objects in a Custom Sequence (utilizing pre-existing methods)

Imagine we have an array of objects: ["c", "a", "b", "d"] Is there a way in ECMAScript or through a third-party JavaScript library to rearrange the objects in the first array to match the order specified by the second array, all within one line or functi ...

Vue - Implementing plugin as a prototype functionality

For notifications in my application, I've incorporated the euvl/vue-notification library. Each time I need to notify the user, I have to include the following code: If I'm within a Vue component: this.$notify({ group: 'panel', ...

An error popped up: "argument list is missing) after"

Encountered an issue: Error message: missing ) after the argument list $('.next-btn').append("<a class="actionsubmit" ng-click="onSubmit('hello.html')">Check</a>"); ...

Encountered difficulties while attempting to use keyboard input and received a null value when attempting to retrieve a data-* attribute using Python and Selenium

Encountered an issue with keyboard interaction and finding a null value for the data-* attribute during automated login attempts on Gmail using Python and Selenium While attempting to automatically log in to Gmail using Python and Selenium, I successfully ...