Loading Non Power of Two Images

Hi,
I’m loading images dynamically from google images, the problem is that they don’t have power-of-two resolution (128,256,512…).
This result is a failure to load the images and the following exception:

WARNING: Invalid filter mode set on non power of two texture. Forcing linear addressing. playcanvas-latest.js:325
4WebGL: drawElements: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'. Or the texture is Float or Half Float type with linear filtering while OES_float_linear or OES_half_float_linear extension is not enabled.

Is there a way to disable this restriction or somehow convert on the fly the image to support power of two resolution?
Any help would be appreciated

To WebGL texture you can provide Image, Canvas and Video elements to get data from.
Saying that, you can create 2D canvas of resolution you need your image to be, but power of 2, then draw your image to it: ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
And then you can use that canvas element fed to texture.

We might look into some more friendly way of doing it from the designer.

Hi there. Non-power of two textures should work fine. Here’s an example:

pc.script.create('texture', function (context) {
    // Creates a new Texture instance
    var Texture = function (entity) {
        this.entity = entity;
    };

    Texture.prototype = {
        // Called once after all resources are loaded and before the first update
        initialize: function () {
            var image = new Image();
            image.crossOrigin = "anonymous";
            image.onload = function () {
                var isPowerOfTwo = pc.math.powerOfTwo(image.width) &&
                                   pc.math.powerOfTwo(image.height);

                var material = this.entity.model.model.getMaterials()[0];
        
                // If a texture is non power of two in both dimensions, it can't be mipmapped!
                var texture = new pc.gfx.Texture(context.graphicsDevice, {
                    autoMipmap: isPowerOfTwo
                });
                texture.minFilter = isPowerOfTwo ? pc.gfx.FILTER_LINEAR_MIPMAP_LINEAR : pc.gfx.FILTER_LINEAR;
                texture.magFilter = pc.gfx.FILTER_LINEAR;
                texture.addressU = isPowerOfTwo ? pc.gfx.ADDRESS_REPEAT : pc.gfx.ADDRESS_CLAMP_TO_EDGE;
                texture.addressV = isPowerOfTwo ? pc.gfx.ADDRESS_REPEAT : pc.gfx.ADDRESS_CLAMP_TO_EDGE;

                texture.setSource(image);
                material.diffuseMap = texture;
                material.update();
            };
            image.src = 'http://some.url.com/image.jpg';
        },

        // Called every frame, dt is time in seconds since last update
        update: function (dt) {
        }
    };

   return Texture;
});

WARNING: I haven’t actually run this code.

Notice how, for non-power of two images, I’m disabling mipmapping and forcing a clamp to edge address mode on the texture.

If you want a repeat texture address mode, or you want mipmapping, you must do as Max says and resize the image to power of two dimensions using the mechanism he describes. It’s pretty easy, but let us know if you need help with it. :smile:

@max
Thanks,It’s a nice trick.

@will
1.Perfect! this worked and now the google images are loaded without an issue.
2.Resizing the pictures to power of 2 on the fly in max’s way shouldn’t be an issue.

1 Like