Textures with dimensions non power of two get always downscaled to power of two dimensions. Previous algorithm was performing a "round" to aproximate power of two dimensions rather than floor.
Example :
r89: THREE.WebGLRenderer: image is not power of two (1638x2166). Resized to 1024x2048
r80: THREE.WebGLRenderer: image is not power of two (1638x2166). Resized to 2048x2048
Code in WebGLTextures.js r89:
function makePowerOfTwo( image ) {
...
canvas.width = _Math.floorPowerOfTwo( image.width );
canvas.height = _Math.floorPowerOfTwo( image.height );
...
}
Code in WebGLTextures.js r80:
function makePowerOfTwo( image ) {
...
canvas.width = _Math.nearestPowerOfTwo( image.width );
canvas.height = _Math.nearestPowerOfTwo( image.height );
...
}
Note: nearestPowerOfTwo is now deprecated and replaced with floorPowerOfTwo.
We did that on purpose. See #12201. What is the problem you are having?
Basically we are using images that are uploaded by client, so we cannot restrict upload dimensions.
This change broked the previous functionality because some textures had sensitive details/dimension , for example textures 500x500 loose a lot of detail when resized to 256x256.
The alternative solution would be to implement our own TextureLoader and resize the image before assigning it to the texture, this solution for some reason results in Gpu memory not being freed right now.
Another solution would be to have someway to configure what resize algorithm/method is used by "makePowerOfTwo" function
Power-of-two textures are not required. Depending on your use case, another option may be:
texture.generateMipmaps = false;
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
texture.minFilter = THREE.LinearFilter;
We use anisotropic filtering, mipmaps are are required...
Even if you're using anisotropic filtering I think you still need mipmaps.
https://gamedev.stackexchange.com/a/101012
Yes, that Is what I was trying to say, that because of the anisotropic filtering we need to have the mipmaps and as a result the power of two dimensions. Regarding the alternative solution, I was trying to create a custom texture loader, code very similar to the built in TextureLoader, just creating a temporary resized canvas and setting it to the texture in the load handler, but for fome reason this way Gpu memory is not released properly, may be related to the Cache being enabled?
what about something like this:
That's quite a hack 馃榿
Regarding the alternative solution, I was trying to create a custom texture loader, code very similar to the built in TextureLoader, just creating a temporary resized canvas and setting it to the texture in the load handler, but for fome reason this way Gpu memory is not released properly, may be related to the Cache being enabled?
So the issue is... I actually don't understand what the issue is anymore.
the issue is he is losing texture details. I guess what he really wants is some texture 'resize policy' setting, Idk:
to have someway to configure what resize algorithm/method
That's quite a hack
would it be less of a hack if 3js had
Texture.resizeMethod = Math.floorPowerOfTwo
and users then could override this function?
This change broked the previous functionality because some textures had sensitive details/dimension , for example textures 500x500 loose a lot of detail when resized to 256x256.
From my point of view, this is something users need to handle in their apps. They should resize the image according to their ideas and then pass the new image to three.js
.
would it be less of a hack if 3js had
Texture.resizeMethod = Math.floorPowerOfTwo
and users then could override this function?
@makc
Yes, this would be a solution.
Also, the proposed hack should work also, given that the "floorPowerOfTwo" function is not used elsewhere in the Three.js :)
From my point of view, this is something users need to handle in their apps. They should resize the image according to their ideas and then pass the new image to three.js
@Mugen87
We have tried as mentioned creating a resized canvas (similar to the code that three.js uses to convert to POT textures), but this solution it not quite optimal in terms of memory, I understand this would be related to issue #12469
So, as the downscale to POT was the intended behavior related to the resizing of the textures, I think this can be closed.
My question : it is possible to make the resize function configurable or have a property that configures what function should be used?
So, as the downscale to POT was the intended behavior related to the resizing of the textures, I think this can be closed.
馃憤
My question : it is possible to make the resize function configurable or have a property that configures what function should be used?
I feel it's a bit of over-engineering...
@mrdoob How about doing something like this instead:
_canvas.width = ( image.width > 1024 ) ? _Math.floorPowerOfTwo( image.width ) : _Math.ceilPowerOfTwo( image.width );
Similarly for height
, and being sure to also test for max texture size.
It may be easier to do some utils for developers instead?
Something like...
var imagePOT = THREE.ImageUtils.createPowerOfTwoImage( image );
That works. I was just suggesting to round large textures down; small textures up.
That works. I was just suggesting to round large textures down; small textures up.
Yeah, I understand. But I bet it wouldn't take long for someone to come with a use-case that doesn't fit that expectation.
Most helpful comment
would it be less of a hack if 3js had
and users then could override this function?