Jump to content

[PixiJS6]Rendering question: The texture becomes blurry as it shrinks.


znw-test
 Share

Recommended Posts

I usr [email protected] and Chrome. However, I found that the texture became very blurry when I zoomed out. The browser img tag, by contrast, is clear. I don't know if I didn't set something right. Can someone take a look at it for me?

poster_bunny_bunnysize.jpg
I use this Image as a sprite's texture. I set the width and height of the Sprite to 0.2 times the width and height of the texture. The results presented are vague.
e6d5c6412655214655480f4d850c42f7.png.d28f0ee9e2109d2b3ebf92a349350a66.png

I wrote a demo and compared the img tag image with the same width and height, and the difference was even more obvious. Can someone tell me what makes such a difference?

My development environment:

Browser: Google Chrome 107.0.5304.110
System: MacOS 10.15.6
Code:

<!-- https://codepen.io/znw-test/pen/oNyMvGg -->
    <script src="https://pixijs.download/v6.2.2/pixi.js"></script>
    <script>
let canvasWidth = 1000
let canvasHeight = 800
const app = new PIXI.Application({ 
  width: canvasWidth, 
  height: canvasHeight, 
  backgroundColor: 0x1099bb,
  });
document.body.appendChild(app.view);

let scaleRatio = .2

let originTexture
let mainSprite
let img

function init(o){
    originTexture = o
    mainSprite = new PIXI.Sprite(originTexture)
    mainSprite.x = 0
    mainSprite.y = 0;
    mainSprite.width = originTexture.width * scaleRatio;
    mainSprite.height = originTexture.height * scaleRatio;
    app.stage.addChild(mainSprite)
}
function changeFile(e){
    let src = URL.createObjectURL(e[0])
    let img = new Image()
    img.src=src
    let originTextureLoader = PIXI.Texture.fromLoader(src)
    originTextureLoader.then((o)=>{
        URL.revokeObjectURL(src)
      img.style.width = (img.naturalWidth * scaleRatio) + 'px'
      img.style.height = (img.naturalHeight * scaleRatio) +'px'
      document.body.appendChild(img)
        originTexture && originTexture.destroy(true)
        mainSprite && mainSprite.destroy({children:true, texture:true, baseTexture:true})
        originTextureLoader = null
        init(o)
    })
}

    </script>
    <div>
        <label>
            Select Image:
            <input type="file" onchange="changeFile(this.files)" />
        </label>
    </div>

 

Link to comment
Share on other sites

15 hours ago, Alexander Kolosov said:

Hi. I checked and I don't have such a problem.
But I noticed that you use resolution: 2. Try to set: 

resolution: window.devicePixelRatio

 

Thank you for your reply.
I have two screens, one with a devicePixelRatio of 1(1920 x 1080) and the other with a devicePixelRatio of 2(2880 x 1800).
I only set the resolution of the application to 2 for both screens to work properly.
I don't know why.

 

I changed my demo. Added a renderTexture and a filter. The filters are set for the stage as the mouse moves into it. The resolution of the filter must be consistent with that of the application; otherwise, the image will be blurred.
https://codepen.io/znw-test/full/oNyMvGg

<script src="https://pixijs.download/v6.2.2/pixi.js"></script>
<script>
  let canvasWidth = 1000
  let canvasHeight = 800
  const app = new PIXI.Application({
    width: canvasWidth,
    height: canvasHeight,
    backgroundColor: 0x1099bb,
    resolution: 2,
  });
  app.renderer.autoDensity = true
  app.renderer.resize(canvasWidth, canvasHeight)
  document.body.appendChild(app.view);
  let scaleRatio = 0.2
  let filter = new PIXI.Filter(null, null)
  filter.resolution = 2;
  app.stage.filterArea = app.screen;
  app.stage.interactive = true
  app.stage.on('mouseover', ()=>{app.stage.filters = [filter]})
  app.stage.on('mouseout', ()=>{app.stage.filters = null})
  let originTexture
  let mainSprite
  let img
  let finalTexture
  let quadSprite
  const VERTEX_SHADER = `
    attribute vec2 a_position;
    attribute vec2 a_texCoord;

    varying vec2 vTexcoord;
    void main() {
        gl_Position = vec4(a_position, 0, 1);
        vTexcoord = (a_texCoord);
    }
`
  const FRAGMENT_SHADER = `
    precision highp float;
    uniform sampler2D uTexture;
    varying vec2 vTexcoord;
    void main() {
        highp vec4 color = texture2D(uTexture, vTexcoord);
        // gl_FragColor = vec4(vec3(1.0,1.0,1.0) - color.rgb, 1.0);
        gl_FragColor = color;
    }    
`
  const geometry = new PIXI.Geometry()
    .addAttribute('a_position', // the attribute name
      [-1, -1, // x, y
        1, -1, // x, y
        1, 1,
        -1, 1
      ], // x, y
      2) // the size of the attribute
    .addAttribute('a_texCoord', // the attribute name
      [0, 0, // u, v
        1, 0, // u, v
        1, 1,
        0, 1
      ], // u, v
      2) // the size of the attribute
    .addIndex([0, 1, 2, 0, 2, 3]);
  const uniforms = {
    uTexture: PIXI.Texture.EMPTY
  };
  const shader = PIXI.Shader.from(VERTEX_SHADER, FRAGMENT_SHADER, uniforms);
  const quad = new PIXI.Mesh(geometry, shader);

  function init(o) {
    originTexture = o
    uniforms.uTexture = originTexture
    finalTexture = PIXI.RenderTexture.create({
      width: originTexture.width,
      height: originTexture.height
    });
    quadSprite = new PIXI.Sprite(finalTexture)
    mainSprite = new PIXI.Sprite(originTexture)
    mainSprite.x = 0
    mainSprite.y = 0
    mainSprite.width = originTexture.baseTexture.realWidth * scaleRatio;
    mainSprite.height = originTexture.baseTexture.realHeight * scaleRatio;
    quadSprite.x = mainSprite.width
    quadSprite.y = 0
    quadSprite.width = originTexture.baseTexture.realWidth * scaleRatio;
    quadSprite.height = originTexture.baseTexture.realHeight * scaleRatio;
    app.renderer.render(quad, {
      renderTexture: finalTexture
    })
    console.log("can add")
    app.stage.addChild(mainSprite)
    app.stage.addChild(quadSprite)
  }

  function changeFile(e) {
    let src = URL.createObjectURL(e[0])
    img && document.body.removeChild(img)
    img = new Image()
    img.src = src
    let originTextureLoader = PIXI.Texture.fromLoader(src, src, null, {
      scaleMode: 1,
      resolution: 1
    })
    originTextureLoader.then((o) => {
      img.style.width = img.naturalWidth * scaleRatio + 'px'
      img.style.height = img.naturalHeight * scaleRatio + 'px'
      document.body.appendChild(img)
      URL.revokeObjectURL(src)
      finalTexture && finalTexture.destroy(true)
      originTexture && originTexture.destroy(true)
      mainSprite && mainSprite.destroy({
        children: true,
        texture: true,
        baseTexture: true
      })
      quadSprite && quadSprite.destroy({
        children: true,
        texture: true,
        baseTexture: true
      })
      originTextureLoader = null
      init(o)
    })
  }
</script>
<div>
  <label>
    Select Image:
    <input type="file" onchange="changeFile(this.files)" />
  </label>
</div>

 

Edited by ZZZZZZZZZ
Fix bug
Link to comment
Share on other sites

Hi. I had a problem with blurring when the mouse focused on the image only when filter.resolution and app.renderer.resolution values are different.
In current https://codepen.io/znw-test/pen/oNyMvGg filter.resolution = 2 and app.renderer.resolution = 2 and it looks good. But if you set the filter.resolution = 1 then when you hover the mouse it will become blurry.

Link to comment
Share on other sites

I think you need to use window.devicepixelratio for renderer and filter resolution. If you use 2 monitors with different DPI, then after moving the browser to another monitor the window.devicePixelRation will be changed and you will be able to update the renderer and filter resolution.

Below is a rough example of how you can do that.

added:

requestAnimationFrame(this.render.bind(this));

 function render() {
    app.renderer.resolution = window.devicePixelRatio;
    filter.resolution = window.devicePixelRatio;
   
    requestAnimationFrame(this.render.bind(this));
  }

 

full code:

<script src="https://pixijs.download/v6.2.2/pixi.js"></script>
<script>
  let canvasWidth = 1000
  let canvasHeight = 800
  const app = new PIXI.Application({
    width: canvasWidth,
    height: canvasHeight,
    backgroundColor: 0x1099bb,
    resolution: window.devicePixelRatio,
  });
  app.renderer.autoDensity = true
  app.renderer.resize(canvasWidth, canvasHeight)
  document.body.appendChild(app.view);
  let scaleRatio = 0.2
  let filter = new PIXI.Filter(null, null)
  filter.resolution = window.devicePixelRatio;
  app.stage.filterArea = app.screen;
  app.stage.interactive = true
  app.stage.on('mouseover', ()=>{app.stage.filters = [filter]})
  app.stage.on('mouseout', ()=>{app.stage.filters = null})
  let originTexture
  let mainSprite
  let img
  let finalTexture
  let quadSprite
  const VERTEX_SHADER = `
    attribute vec2 a_position;
    attribute vec2 a_texCoord;

    varying vec2 vTexcoord;
    void main() {
        gl_Position = vec4(a_position, 0, 1);
        vTexcoord = (a_texCoord);
    }
`
  const FRAGMENT_SHADER = `
    precision highp float;
    uniform sampler2D uTexture;
    varying vec2 vTexcoord;
    void main() {
        highp vec4 color = texture2D(uTexture, vTexcoord);
        // gl_FragColor = vec4(vec3(1.0,1.0,1.0) - color.rgb, 1.0);
        gl_FragColor = color;
    }    
`
  const geometry = new PIXI.Geometry()
    .addAttribute('a_position', // the attribute name
      [-1, -1, // x, y
        1, -1, // x, y
        1, 1,
        -1, 1
      ], // x, y
      2) // the size of the attribute
    .addAttribute('a_texCoord', // the attribute name
      [0, 0, // u, v
        1, 0, // u, v
        1, 1,
        0, 1
      ], // u, v
      2) // the size of the attribute
    .addIndex([0, 1, 2, 0, 2, 3]);
  const uniforms = {
    uTexture: PIXI.Texture.EMPTY
  };
  const shader = PIXI.Shader.from(VERTEX_SHADER, FRAGMENT_SHADER, uniforms);
  const quad = new PIXI.Mesh(geometry, shader);

  function init(o) {
    originTexture = o
    uniforms.uTexture = originTexture
    finalTexture = PIXI.RenderTexture.create({
      width: originTexture.width,
      height: originTexture.height
    });
    quadSprite = new PIXI.Sprite(finalTexture)
    mainSprite = new PIXI.Sprite(originTexture)
    mainSprite.x = 0
    mainSprite.y = 0
    mainSprite.width = originTexture.baseTexture.realWidth * scaleRatio;
    mainSprite.height = originTexture.baseTexture.realHeight * scaleRatio;
    quadSprite.x = mainSprite.width
    quadSprite.y = 0
    quadSprite.width = originTexture.baseTexture.realWidth * scaleRatio;
    quadSprite.height = originTexture.baseTexture.realHeight * scaleRatio;
    app.renderer.render(quad, {
      renderTexture: finalTexture
    })
    console.log("can add")
    app.stage.addChild(mainSprite)
    app.stage.addChild(quadSprite)
    
    requestAnimationFrame(this.render.bind(this));
  }
  
  function render() {
    app.renderer.resolution = window.devicePixelRatio;
    filter.resolution = window.devicePixelRatio;
    
    requestAnimationFrame(this.render.bind(this));
  }

  function changeFile(e) {
    let src = URL.createObjectURL(e[0])
    img && document.body.removeChild(img)
    img = new Image()
    img.src = src
    let originTextureLoader = PIXI.Texture.fromLoader(src, src, null, {
      scaleMode: 1,
      resolution: 1
    })
    originTextureLoader.then((o) => {
      img.style.width = img.naturalWidth * scaleRatio + 'px'
      img.style.height = img.naturalHeight * scaleRatio + 'px'
      document.body.appendChild(img)
      URL.revokeObjectURL(src)
      finalTexture && finalTexture.destroy(true)
      originTexture && originTexture.destroy(true)
      mainSprite && mainSprite.destroy({
        children: true,
        texture: true,
        baseTexture: true
      })
      quadSprite && quadSprite.destroy({
        children: true,
        texture: true,
        baseTexture: true
      })
      originTextureLoader = null
      init(o)
    })
  }
</script>
<div>
  <label>
    Select Image:
    <input type="file" onchange="changeFile(this.files)" />
  </label>
</div>

 

Link to comment
Share on other sites

1 hour ago, Alexander Kolosov said:

I think you need to use window.devicepixelratio for renderer and filter resolution. If you use 2 monitors with different DPI, then after moving the browser to another monitor the window.devicePixelRation will be changed and you will be able to update the renderer and filter resolution.

Thank you for your reply.

========== update ========

requestAnimationFrame(this.render.bind(this));

 function render() {
    app.renderer.resolution = window.devicePixelRatio;
    filter.resolution = window.devicePixelRatio;
	app.renderer.resize(canvasWidth, canvasHeight); // add this line
   
    requestAnimationFrame(this.render.bind(this));
  }

After add resize code, the picture1 and picture2 problem solved. But the picture3 problem remains.

========== original ========

But this switch has a strange effect. If I insert an image on a screen with devicePixelRatio 2 and drag it onto a screen with devicePixelRatio 1, the stage will shrink.

pixi_in2.thumb.png.e43d630bcc862139a4a386ef35eff905.png

Picture 1: App init  on a screen with a devicePixelRatio of 2.

pixi_in1.thumb.png.e2e8baaeff3cf3547302cdc7447b5ff0.png

Picture 2: Move the browser to a screen with a devicePixelRatio of 1.

 

But if I refresh the page and insert an image on a screen with a devicePixelRatio of 1, the image appears blurry. See Picture 3.

image.png.a5614572682175d4e2999661243841ea.png

Picture 3: App init  on a screen with a devicePixelRatio of 1.

Edited by znw-test
Additional Information
Link to comment
Share on other sites

6 hours ago, ivan.popelyshev said:

turn off mipmaps maybe?

Thank you for your reply.

I tried to set MIPMAP_TEXTURES to OFF before instantiating the Application but it didn't seem to work out any better.

PIXI.settings.MIPMAP_TEXTURES = PIXI.MIPMAP_MODES.OFF;
const app = new PIXI.Application({
  width: canvasWidth,
  height: canvasHeight,
  backgroundColor: 0x1099bb,
  resolution: window.devicePixelRatio,
});

 

Link to comment
Share on other sites

On 12/1/2022 at 9:15 PM, znw-test said:

Thank you for your reply.

I tried to set MIPMAP_TEXTURES to OFF before instantiating the Application but it didn't seem to work out any better.

PIXI.settings.MIPMAP_TEXTURES = PIXI.MIPMAP_MODES.OFF;
const app = new PIXI.Application({
  width: canvasWidth,
  height: canvasHeight,
  backgroundColor: 0x1099bb,
  resolution: window.devicePixelRatio,
});

 

did you try remaster your img with contraint of POW2 ?
 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...