Как масштабировать текстуру в webgl? - PullRequest
0 голосов
/ 26 сентября 2018

У меня есть текстура размером 800х600.Как мне масштабировать его на webgl <canvas> в другом размере и сохранить исходное соотношение сторон?Предполагая, что буфер рисования и холст имеют одинаковые размеры.

1 Ответ

0 голосов
/ 26 сентября 2018

Учитывая, что WebGL заботится только о координатах clipsapce, вы можете просто нарисовать квадрат из 2 единиц (от -1 до +1) и масштабировать его в соответствии с аспектом холста по сравнению с аспектом изображения.

В другихслова

  const canvasAspect = canvas.clientWidth / canvas.clientHeight;
  const imageAspect = image.width / image.height;

  let scaleY = 1;
  let scaleX = imageAspect / canvasAspect;

Обратите внимание, что вам нужно решить, как вы хотите соответствовать изображению.scaleY= 1 означает, что изображение всегда будет соответствовать по вертикали, а по горизонтали будет таким, каким оно будет.

Если вы хотите, чтобы оно соответствовало по горизонтали, вам нужно сделать scaleX = 1

  let scaleX = 1;
  let scaleY = canvasAspect / imageAspect;

Если вы хотите contain, то

  let scaleY = 1;
  let scaleX = imageAspect / canvasAspect;
  if (scaleX > 1) {
    scaleY = 1 / scaleX;
    scaleX = 1;
  }

Если вы хотите cover, тогда

  let scaleY = 1;
  let scaleX = imageAspect / canvasAspect;
  if (scaleX < 1) {
    scaleY = 1 / scaleX;
    scaleX = 1;
  }

let scaleMode = 'fitV';

const gl = document.querySelector("canvas").getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 u_matrix;
varying vec2 v_texcoord;
void main() {
  gl_Position = u_matrix * position;
  v_texcoord = position.xy * .5 + .5;  // because we know we're using a -1 + 1 quad
}
`;
const fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_tex;
void main() {
  gl_FragColor = texture2D(u_tex, v_texcoord);
}
`;

let image = { width: 1, height: 1 }; // dummy until loaded
const tex = twgl.createTexture(gl, {
  src: 'https://i.imgur.com/TSiyiJv.jpg',
  crossOrigin: 'anonymous',
}, (err, tex, img) => {
  // called after image as loaded
  image = img;
  render();
});

const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: {
    numComponents: 2,
    data: [
      -1, -1,  // tri 1
       1, -1,
      -1,  1,      
      -1,  1,  // tri 2
       1, -1,
       1,  1,
    ],
  }
});


function render() {
  // this line is not needed if you don't
  // care that the canvas drawing buffer size
  // matches the canvas display size
  twgl.resizeCanvasToDisplaySize(gl.canvas);

  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);

  const canvasAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const imageAspect = image.width / image.height;
  let scaleX;
  let scaleY;

  switch (scaleMode) {
    case 'fitV':
      scaleY = 1;
      scaleX = imageAspect / canvasAspect;
      break;
    case 'fitH':
      scaleX = 1;
      scaleY = canvasAspect / imageAspect;
      break;
    case 'contain':
      scaleY = 1;
      scaleX = imageAspect / canvasAspect;
      if (scaleX > 1) {
        scaleY = 1 / scaleX;
        scaleX = 1;
      }
      break;
    case 'cover':
      scaleY = 1;
      scaleX = imageAspect / canvasAspect;
      if (scaleX < 1) {
        scaleY = 1 / scaleX;
        scaleX = 1;
      }
      break;
  }
  
  twgl.setUniforms(programInfo, {
    u_matrix: [
      scaleX, 0, 0, 0,
      0, -scaleY, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1,
    ],
  });
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}

render();
window.addEventListener('resize', render);
document.querySelectorAll('button').forEach((elem) => {
  elem.addEventListener('click', setScaleMode);
});

function setScaleMode(e) {
 scaleMode = e.target.id;
 render();
}
html, body { 
  margin: 0;
  height: 100%;
}
canvas {
  width: 100%;
  height: 100%;
  display: block;
}
.ui {
  position: absolute;
  left: 0;
  top: 0;
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
<div class="ui">
  <button id="fitV">fit vertical</button>
  <button id="fitH">fit horizontal</button>
  <button id="contain">contain</button>
  <button id="cover">cover</button>
</div>

Приведенный выше код использует матрицу 4x4 для применения шкалы

  gl_Position = u_matrix * position;

Она также может легко перейти в шкалунапрямую

  uniform vec2 scale;
  ...
  gl_Position = vec4(scale * position.xy, 0, 1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...