Texture is not power of two / Can't change texture

Quick run down - I’m trying to build a gallery of 360 images, when a gallery image is clicked, modal opens with 360 viewer inside, but I want the images to change dynamically based on the gallery image that has been clicked.

> THREE.WebGLRenderer: image is not power of two (1920x960). Resized to 2048x1024 
> blob:http://nolan.bfdevserver.com/7ca91892-2e3b-4fff-b672-ec250ef13498 new image blob
> blob:http://nolan.bfdevserver.com/7788013c-9611-497a-bc12-66a06bb2244f old image blob
> THREE.WebGLRenderer: image is not power of two (0x0). Resized to 0x0 
> THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. 
> THREE.Texture
> WebGL: INVALID_VALUE: texImage2D: no canvas

So I’m not sure why this is happening.
Here’s my code. Also a codepen of said code - https://codepen.io/NolWag/pen/GbwEvY

```
<div id="container" data="<?php $ThreeSixtyImage = the_field('image'); ?>"></div>

<div id="gallery-modal" class="modal">
</div> 

<div id="gallery" class="gallery">
    <div class="gallery-item">
        <img src="https://www.bluefire360.com/wp-content/uploads/V4260450-min.jpg" />
    </div>
    <div class="gallery-item">
        <img src="http://nolan.bfdevserver.com/wp-content/uploads/pano_2048.jpg" />
    </div>
    <div class="gallery-item">
        <img src="http://nolan.bfdevserver.com/wp-content/uploads/pano_2048.jpg" />
    </div>
</div>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
```
 var camera, scene, renderer;
        var Container = document.getElementById('container');
        var imageFile = Container.getAttribute('data');

        var isUserInteracting = false,
        onMouseDownMouseX = 0, onMouseDownMouseY = 0,
        lon = 0, onMouseDownLon = 0,
        lat = 0, onMouseDownLat = 0,
        phi = 0, theta = 0;

        init();
        animate();


        function init() {

            var container, mesh;

            container = document.getElementById( 'gallery-modal' );

            camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1100 );
            camera.target = new THREE.Vector3( 0, 0, 0 );

            scene = new THREE.Scene();

            var geometry = new THREE.SphereGeometry( 500, 60, 40 );
            geometry.scale( - 1, 1, 1 );

            var material = new THREE.MeshBasicMaterial( {
                map: new THREE.TextureLoader().load(  imageFile )
            } );

            material.minFilter = THREE.LinearFilter;

            mesh = new THREE.Mesh( geometry, material );

            scene.add( mesh );

            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );
            //renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.setSize( window.innerWidth, 500);
            container.appendChild( renderer.domElement );


            // Controls to move the image
            document.addEventListener( 'mousedown', onDocumentMouseDown, false );
            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            document.addEventListener( 'mouseup', onDocumentMouseUp, false );
            document.addEventListener( 'wheel', onDocumentMouseWheel, false );

            document.addEventListener( 'dragover', function ( event ) {

                event.preventDefault();
                event.dataTransfer.dropEffect = 'copy';

            }, false );

            document.addEventListener( 'dragenter', function ( event ) {

                document.body.style.opacity = 0.5;

            }, false );

            document.addEventListener( 'dragleave', function ( event ) {

                document.body.style.opacity = 1;

            }, false );


            /// ADDITIONAL CODE STARTS HERE

            var galleryDOM = document.getElementById('gallery');
            var modal = document.getElementById('gallery-modal');

                galleryDOM.addEventListener('click', function(e) {

                    if(e.target.localName == 'img') {

                        // Show Modal
                        modal.style.display = "block";

                        // Create blob from image url
                        var blob = new Blob([e.target.src], {type: 'url'});             
                        var newestImage = window.URL.createObjectURL(blob);

                        console.log(newestImage,  "new image blob")
                        console.log(material.map.image.src,  "old image blob")

                        // Apply newestImage to material object in THREEjs
                        if(newestImage) {
                            material.map.image.src = newestImage;
                            material.map.needsUpdate = true;

                        }

                    }
                });


                window.onclick = function(event) {
                    if (event.target == modal) {
                        modal.style.display = 'none';
                    }
                }

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

        }

        function dataURItoBlob(dataURI) {
            var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
            var binary = atob(dataURI.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''));
            var array = [];
            for (var i = 0; i < binary.length; i++) {
                array.push(binary.charCodeAt(i));
            }
            return new Blob([new Uint8Array(array)], {type: mime});
        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize( window.innerWidth, window.innerHeight );

        }

        function onDocumentMouseDown( event ) {

            event.preventDefault();

            isUserInteracting = true;

            onPointerDownPointerX = event.clientX;
            onPointerDownPointerY = event.clientY;

            onPointerDownLon = lon;
            onPointerDownLat = lat;

        }

        function onDocumentMouseMove( event ) {

            if ( isUserInteracting === true ) {

                lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
                lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;

            }

        }

        function onDocumentMouseUp( event ) {

            isUserInteracting = false;

        }

        function onDocumentMouseWheel( event ) {

            camera.fov += event.deltaY * 0.05;
            camera.updateProjectionMatrix();

        }

        function animate() {

            requestAnimationFrame( animate );
            update();

        }

        function update() {

            if ( isUserInteracting === false ) {

                lon += 0.1;

            }

            lat = Math.max( - 85, Math.min( 85, lat ) );
            phi = THREE.Math.degToRad( 90 - lat );
            theta = THREE.Math.degToRad( lon );

            camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
            camera.target.y = 500 * Math.cos( phi );
            camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );

            camera.lookAt( camera.target );

            /*
            // distortion
            camera.position.copy( camera.target ).negate();
            */

            renderer.render( scene, camera );

        }

Hi!
Any specific reason to use r79?

if(newestImage) {
    material.map.image.src = newestImage;
    material.map.needsUpdate = true;
}

Here you change .src and try to update .map immediately, without waiting till the image loaded.

It should be something like that:

material.map.image.onload = () => {
    material.map.needsUpdate = true;
}

if(newestImage) {
    material.map.image.src = newestImage;
}

But why simply not use several textures?

I’m new to Three.js so I’m not really sure what that is, I’m reusing code that isn’t mine.

I am totally open to suggestions if you see an easier route!

Thanks for the help

It should be:

material.map.minFilter = THREE.LinearFilter;

This a very outdated version, you should use the latest to get the best result, compatibility and to avoid any bugs which has been resolved meanwhile.

Your images you load aren’t power of two, by having them in the proper size as THREE resized them for you

you won’t get those warnings anymore and avoid lags, since resizing large images can be slow.

1 Like

Okay, so this got rid of the power of two error. I’m getting a new one now -

WebGL: INVALID_VALUE: texImage2D: bad image data

Also, I switched to this method, but the update never fires and when I force it to, I get the error above.

if(newestImage) {
								console.log('newestImage');
								material.map.image.src = newestImage;
							}

							material.map.image.onload = () => {
								console.log('update');
								material.map.needsUpdate = true;
							}

Ahhh gotcha, I didn’t even notice. Thank you.

For anyone that is curious, I figured it out and the answer is here -

Thanks for all of the help!

Tom Hanks is developing software?!? :astonished:

3 Likes