Как растянуть холст WebGL без размытия?Стиль «рендеринг изображений» не работает - PullRequest
0 голосов
/ 28 января 2019

Я создал холст с width=16 и height=16.Затем я использовал WebGL для рендеринга изображения.Вот как это выглядит:

enter image description here

После этого я масштабировал холст, используя width: 256px и height: 256px.Я также установил image-rendering на pixelated:

      canvas {
        image-rendering: optimizeSpeed;             /* STOP SMOOTHING, GIVE ME SPEED  */
        image-rendering: -moz-crisp-edges;          /* Firefox                        */
        image-rendering: -o-crisp-edges;            /* Opera                          */
        image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */
        image-rendering: pixelated; /* Chrome */
        image-rendering: optimize-contrast;         /* CSS3 Proposed                  */
        -ms-interpolation-mode: nearest-neighbor;   /* IE8+                           */
        width: 256px;
        height: 256px;
      }

Это результат:

enter image description here

Изображение размытое,Зачем?Я использую Safari 12.0.2 на OSX Mojave.

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Safari пока не поддерживает image-rendering: pixelated; в WebGL. Подана ошибка

Также crisp-edges нет! = pixelated.crisp-edges может быть любым количеством алгоритмов.Это не значит pixelated.Это означает, что применяет некоторый алгоритм, который сохраняет четкие края , из которых существует множество алгоритмов.

Сама спецификация показывает примеры:

Для данного изображения:

enter image description here

Это pixelated:

enter image description here

Гдев качестве браузера разрешено использовать различные алгоритмы для crisp-edges, например, результат может быть

enter image description here

Другими словами, ваш CSS можетне дает ожидаемых результатов.Если браузер не поддерживает пиксельную графику, но поддерживает четкие края, и если они используют алгоритм, подобный описанному выше, то пиксельный внешний вид не будет.

Самый эффективный способ рисовать пиксельную графику без image-rendering: pixelated - этонарисовать небольшую текстуру, а затем нарисовать эту текстуру на холсте с фильтрацией NEAREST.

const vs = `
attribute vec4 position;
void main() {
  gl_Position = position;
}
`;
const fs = `
precision mediump float;
void main() {
  gl_FragColor = vec4(1, 0, 0, 1);
}
`;

const screenVS = `
attribute vec4 position;
varying vec2 v_texcoord;
void main() {
  gl_Position = position;
  // because we know position goes from -1 to 1
  v_texcoord = position.xy * 0.5 + 0.5;
}
`;
const screenFS = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_tex;
void main() {
  gl_FragColor = texture2D(u_tex, v_texcoord);
}
`;

const gl = document.querySelector('canvas').getContext('webgl', {antialias: false});

// compile shaders, link programs, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const screenProgramInfo = twgl.createProgramInfo(gl, [screenVS, screenFS]);


const width = 16;
const height = 16;
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);

// create buffers and put data in
const quadBufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: { 
    numComponents: 2,
    data: [
      -1, -1, 
       1, -1,
      -1,  1,
      -1,  1, 
       1, -1,
       1,  1,
    ],
  }
});


render();

function render() {
  // draw at 16x16 to texture
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  gl.viewport(0, 0, width, height);
  gl.useProgram(programInfo.program);
  // bind buffers and set attributes
  twgl.setBuffersAndAttributes(gl, programInfo, quadBufferInfo);
  
  gl.drawArrays(gl.TRIANGLES, 0, 3);  // only draw the first triangle
  
  // draw texture to canvas
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.useProgram(screenProgramInfo.program);
  // bind buffers and set attributes
  twgl.setBuffersAndAttributes(gl, screenProgramInfo, quadBufferInfo);
  // uniforms default to 0 so in this simple case
  // no need to bind texture or set uniforms since
  // we only have 1 texture, it's on texture unit 0
  // and the uniform defaults to 0
  
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}
<canvas width="256" height="256"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

Примечание. Если для рендеринга 3D или по какой-либо другой причине требуется буфер глубины, вам необходимо добавить вложение буфера глубины рендеринга в кадровый буфер.

Обратите внимание, что optimizeSpeed также не является реальным вариантом.Это давно устарело и, как crisp-edges, браузер должен интерпретировать.

0 голосов
/ 28 января 2019

Это очень старая ошибка Webkit , еще до того, как произошел Blink форк.С тех пор Blink исправил это , Webkit все еще не сделал.
Вы можете сообщить им, что это все еще проблема, комментируя все еще открытую проблему.

Что касаетсяОбходной путь есть несколько, но не идеальный.

  • Первый - нарисовать вашу сцену в правильном размере напрямую и сделать пикселизацию самостоятельно.
  • Другойбыть для рендеринга вашего холста webgl на 2d холсте (размер которого вы бы изменили, используя свой трюк CSS, или отрисовать прямо в правильном размере, используя свойство 2d context imageSmoothingEnabled .
  • CSS-Houdini будетвозможно, мы позволим нам обойти эту проблему сами.

Но реальная проблема здесь состоит в том, чтобы выяснить, нужен ли вам этот обходной путь. Я не вижу смысла тестировать этот случай (по крайней мере, безГудини), так что это означает, что вам придется либо выполнять некрасивое обнаружение пользовательского агента, либо применять обходной путь для всех.

...