Conjuring a Spectacular Illusion with ShaderMaterial in Threejs

Trying to implement a texture on a large scaled plane using RepeatWrapping. MeshBasicMaterial works well, but ShaderMaterial causes flickering. Here is the code snippet:

    <!DOCTYPE html>
<html>
<head>
  <title>MeshShaderMaterialExample</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">

  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
  <script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b7c3dfc5d2d2f787998f829987">[email protected]</a>/examples/js/controls/TrackballControls.js"></script>
<script src="js/TWEEN.js"></script>
  <style type="text/css">
    body {
      width: 100%;
      height: 100%;
      background-color: #000;
      color: #fff;
      margin: 0px;
      padding: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
<script>
  var camera, scene, renderer;
var container, mesh, geometry;
var controls, effect;
  var tweenUpdate="false";
  var tweenOver="true";
  var textureData=
{
  "texture_0":
  {
    "img":"gman.png"
  },
  "texture_1":
  {
    "img":"gman.png"
  }}
var magicPosition = { magicTrans:0 };
var magicTarget = { magicTrans:1 };
var magicTween = new TWEEN.Tween(magicPosition).to(magicTarget, 1000);
magicTween.easing(TWEEN.Easing.Linear.None);
var currentTexture=0;
var nextTexture=0;
var uniforms = {
  textures: {
    value: []
  },
  repeat: {
      type: 'f',
      value: 100
  },
  transition: { 
    value: 0
  },
  currentUniform: { 
    value: 0
  },
  nextUniform: { 
    value: 0
  }
};

var textureLoader = new THREE.TextureLoader();

var pics=[];
for (var i = 0; i < Object.keys(textureData).length; i++) {
  var ass="texture_"+i;
  pics[i]= textureData[ass].img;
  console.log(pics[i]);
}
pics.forEach((p, idx)=>{
  textureLoader.load(p, function(tex){
    tex.needsUpdate = true;
    uniforms.textures.value[idx] = tex;
uniforms.textures.value[idx].needsUpdate = true;
// console.log(tex);
        uniforms.textures.value[idx].minFilter = THREE.LinearFilter;

  })
});
var vertShader = `
    varying vec2 vUv;
    uniform float repeat;
    void main()
    {

      vUv = repeat * uv;

      vec4 mvPosition = modelViewMatrix * vec4(position, 1 );
      gl_Position = projectionMatrix * mvPosition;

    }
  `;
var fragShader = `
    uniform sampler2D textures[` + pics.length + `];
    uniform float transition;
    uniform float currentUniform;
    uniform float nextUniform;
    varying vec2 vUv;

    vec4 getTexture(int index){
      for(int i = 0; i < ` + pics.length + `; i++){
         if (i == index){ return texture2D(textures[i],vUv); }
      }
    }
    void main()
    {

        float chunk = 1. / ` + 1 + `.; // amount of transitions = 1
        float t = floor(transition / chunk);
        int idx0 = int(currentUniform);
        int idx1 = int(nextUniform); 
        gl_FragColor = mix(
          getTexture(idx0),
          getTexture(idx1),
          (transition - (float(t) * chunk)) * ` + 1 + `.
        );


    }
  `;

  window.onload=function()
{
  init();
  animate();
}

function init(){


  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );

  controls = new THREE.TrackballControls( camera,renderer.domElement  );
camera.position.z = 500;
  console.log(camera.fov);

  scene = new THREE.Scene();


  scene.add(camera);
  var magicPlaneMaterial = new THREE.ShaderMaterial({
  uniforms: uniforms,
  vertexShader: vertShader,
  fragmentShader: fragShader,
    side: THREE.DoubleSide
  });

  for (var i = 0; i < Object.keys(textureData).length; i++) {
    uniforms.textures.value[i].wrapS = uniforms.textures.value[i].wrapT = THREE.RepeatWrapping;
    uniforms.textures.value[i].needsUpdate = true;

  }

  var magicPlaneGeometry = new THREE.PlaneBufferGeometry(1000, 1000, 16, 16);

  var magicPlaneMesh = new THREE.Mesh(magicPlaneGeometry, magicPlaneMaterial);
  magicPlaneMesh.position.y = -500;
  magicPlaneMesh.rotation.x = Math.PI / 2;
  magicPlaneMesh.scale.x=10;
  magicPlaneMesh.scale.y=10;
  scene.add(magicPlaneMesh);
  changeMagicPlane(currentTexture);
  document.addEventListener( 'wheel', onDocumentMouseWheel, false );

  window.addEventListener( 'resize', onWindowResize, false );
}


function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}

function onDocumentMouseWheel( event ) {
  var fov = camera.fov + event.deltaY * 0.05;

  camera.fov = THREE.Math.clamp( fov, 10, 75 );
  console.log(camera.fov);

  camera.updateProjectionMatrix();
}

function animate() {
    if(tweenUpdate=="true")
  {
    TWEEN.update();
  }
    renderer.render( scene, camera );
  controls.update();
  requestAnimationFrame( animate );
}
function changeMagicPlane(asset){
  var assNum= parseInt(asset);    
  nextTexture = assNum;
  uniforms.nextUniform.value = nextTexture; 

  magicTween.start(); 
  tweenUpdate="true";
  tweenOver="false";
}
magicTween.onUpdate(function(){
uniforms.transition.value = magicPosition.magicTrans;
});
magicTween.onComplete(function(){
  tweenUpdate="false";
  tweenOver="true";

    magicPosition.magicTrans=0;
    currentTexture=nextTexture;
    uniforms.currentUniform.value = currentTexture;
    console.log("Current: "+currentTexture);
});
</script>
</body>
</html>

Utilizing ShaderMaterial for crossfading with a 256x256 pixel texture image.

Code includes Tween.js from () and uses gman.png available at (https://i.sstatic.net/CbEMh.png)

Answer №1

Trilinear texture filtering (mipmaps) has been disabled, as the texture minifying function has been set to THREE.LinearFilter:

uniforms.textures.value[idx].minFilter = THREE.LinearFilter;

This decision results in the appearance of Moire effects.

To enable trilinear texture filtering back with THREE.LinearMipMapLinearFilter (default setting):

uniforms.textures.value[idx].minFilter = THREE.LinearMipMapLinearFilter;

If your fragment shader code exhibits undefined behavior causing mip-mapping to malfunction:

vec4 getTexture(int index){
     for(int i = 0; i < ` + pics.length + `; i++){
        if (i == index){ return texture2D(textures[i],vUv); }
     }
}

void main()
{
    // ....

    gl_FragColor = mix(
         getTexture(idx0),
         getTexture(idx1),
         (transition - (float(t) * chunk)) * ` + 1 + `.
    );

Refer to page 107 of OpenGL ES Shading Language 1.00 Specification for more information on samplers and arrays:

5 Indexing of Arrays, Vectors and Matrices

[...]

Samplers

GLSL ES 1.00 supports both arrays of samplers and arrays of structures which contain samplers. In both these cases, for ES 2.0, support for indexing with a constant-index-expression is mandated but support for indexing with other values is not mandated.

[...]

6 Texture Accesses

Accessing mip-mapped textures within the body of a non-uniform conditional block gives an undefined value. A non-uniform conditional block is a block whose execution cannot be determined at compile time.

To ensure proper texture lookup, perform it within the block scope of main using a constant-index-expression for the index of the texture sampler array:

For example:

float a = transition - float(t) * chunk;
gl_FragColor = mix(texture2D(textures[0], vUv), texture2D(textures[1], vUv), a);

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

Calculating the total sum of values using a dropdown list as a filter with J

I have successfully implemented a dropdown filter in my jQuery datatable to filter data. However, I am now looking to calculate the sum of all values in the filtered table each time a user selects a value from the dropdown list. How can I achieve this? My ...

What is the difference between TypeScript's import/as and import/require syntax?

In my coding project involving TypeScript and Express/Node.js, I've come across different import syntax options. The TypeScript Handbook suggests using import express = require('express');, while the typescript.d.ts file shows import * as ex ...

Tree-pruning nested modules using webpack 4

Having trouble figuring out why tree-shaking isn't working as expected... My main objective is to create a library that can be optimized using tree shaking. index.js Header Button => ButtonGroups I have an application configured with webpack ...

How do I include an icon on the far left of my NavBar menu? I'm having trouble figuring out how to add an icon to the header NavBar

How can I add an icon to the left of my NavBar header? I am struggling with adding an icon on the far left side of my NavBar. The NavBar is a custom class from NavBar.js. I want to include an icon in this bar on the leftmost side. I have already added b ...

Discover which npm module includes the lodash dependency

I've encountered a peculiar situation while using webpack to create a production bundle for my application. Even though I haven't explicitly installed `lodash` and it's not listed in my package.json file, I noticed that it's being added ...

Is there a way to ensure that a statement will not execute until the completion of a preceding function?

I am encountering an issue where the window.open function is being called too quickly, causing my other function not to finish and post in time within my onclick event. I attempted to address this by setting a timeout on the trackData() function, but it o ...

NPM issue: unable to locate module 'internal/fs' in Node.js

Encountering NPM error after updating to Node version 7.x. npm is now non-functional and the cause remains unidentified. Possible reason for the issue could be - npm ERR! Cannot find module 'internal/fs'. The output generated when execu ...

Craft an Interactive Content Carousel

I have been searching for a responsive slide solution for two days with no luck, so I am reaching out for help. I am looking for a simple jQuery code for a slideshow that is lightweight and doesn't require a plugin. Here is the code for the slideshow: ...

What is the best way to achieve the effect of "overflow: hidden" in three.js or WebGL?

I'm diving into the world of designing a WebGL GUI for my games and I'm keen to explore GPU graphics in more depth, as it offers a smoother experience compared to WebKit CSS rendering. Is there a way to create a scrollview where the internal mes ...

Encountering a 404 XHR Error when attempting to add a component in Angular 4.1.0 within Plunker

Having some trouble setting up Angular 4 on Plunker and adding a new component. The following URL is where I'm working: https://plnkr.co/edit/1umcXTeug2o6eiZ89rLl?p=preview I've just created a new component named mycomponent.ts with the necessar ...

What is the best way to include a new line or HTML code in this particular JavaScript function?

I am attempting to create a typewriter effect and I have everything working except for creating new lines. I've tried using the following: \n \r <br /> but it just types those characters instead of creating a new line. Here is my j ...

The depth buffer in Webgl FrameBuffer is not being cleared properly

Currently, I am working on developing a 2D sprite renderer that utilizes render textures for custom compositing. However, I have encountered an issue where the depth buffer on the FrameBuffer is not clearing properly. Due to this, all the sprites leave a p ...

Unable to establish a remote connection to the dlib webserver, but local host connection is functioning

Currently, I am in the process of building a front end using HTML and JavaScript with a C++ backend to handle all calculations. The two are connected through an integrated dlib web server that manages requests. When the frontend requests data, it looks lik ...

RetrieveByUserIdentifier as a callback method (express)

Can you help me refactor the code below to use a callback function instead? I want to ensure that the Req and Res logic is handled separately. Userservice.js function getByUserId(req, res, next) { let userIDD = req.body.userID; User.findOne({ use ...

moodle - eliminate the need for grading at the same time

I'm currently setting up a Moodle installation and I'm looking for suggestions on how to prevent simultaneous grading. My goal is to have several evaluators grading students across various courses without any conflicts. If you have any recommend ...

Refresh the cumulative URL count in JavaScript following the completion of an AJAX submission

My shopping cart is filled with URLs that include a total key: The total value in the cart is <span id="cart-status" >1805.32</span> <ul> <li><a href='/Store/Category/Products?user=ADMIN&total=1805.32'& ...

How can you alter a property within an array of records retrieved from a Mongoose query?

const images = await tbl .find({ creator_id: req.user._id, }) .select({ creator_id: 0, }) .exec() .then((images) => images.forEach((image) => { image.file_name = process.env.IMAGE_ ...

Execute the javascript function asynchronously

I need to call the constGrid(arg1) function 3 times to set up an extjs grid when my form loads. There are other fields on my page as well. I want to ensure that the page does not hang or wait for the method to complete. onLoad(function() { for (var i= ...

Altering the hue of various stroke patterns

I've encountered an issue where I want to create skill bars that display my skills in percentages. Luckily, I found a great code that allows me to do this. Although I would prefer to do it myself, I haven't come across a good tutorial that teache ...

Is it possible to modify a single value in a React useState holding an object while assigning a new value to the others?

In my current state, I have the following setup: const [clickColumn, setClickColumn] = useState({ name: 0, tasks: 0, partner: 0, riskFactor: 0, legalForm: 0, foundationYear: 0 }) Consider this scenario where I only want to update ...