Почему мой вершинный шейдер игнорирует данные Uint8Array, но не данные Float32Array? - PullRequest
0 голосов
/ 13 марта 2020

Мой вершинный шейдер имеет следующий атрибут:

attribute float a_color;

У меня есть буфер массива (память WebAssembly), который состоит только из 0 и 1, и я создаю его представление, создав Uint8Array. Однако мой вершинный шейдер его игнорирует (ошибок нет, но, похоже, все обрабатывает как 0). Я использую twgl. Вот мой код:

Вершинный шейдер:

attribute vec2 a_position;
attribute float a_color;
uniform vec2 u_resolution;
uniform float u_point_width;
varying vec4 v_color;

vec2 normalize_coords(vec2 position) {
   float x = position[0];
   float y = position[1];
   float resx = u_resolution[0];
   float resy = u_resolution[1];
   return vec2(2.0 * x / resx - 1.0, 2.0 * y / resy - 1.0);
}

void main() {
   gl_PointSize = u_point_width;
   vec2 coords = normalize_coords(a_position);
   gl_Position = vec4(coords * vec2(1, -1), 0, 1);
   v_color = vec4(0, a_color ,0,1);
}

Фрагмент шейдера:

precision highp float;

varying vec4 v_color;
void main() {
  gl_FragColor = v_color;
}

Javascript:

  const canvas = document.getElementById("c");

  const CELL_SIZE = 10;

  const gl = canvas.getContext("webgl");
  const programInfo = twgl.createProgramInfo(gl, [
    "vertex-shader",
    "fragment-shader"
  ]);

  twgl.setDefaults({ attribPrefix: "a_" });

  twgl.resizeCanvasToDisplaySize(gl.canvas, window.devicePixelRatio);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  const universe = Universe.new(
    Math.ceil(gl.canvas.width / CELL_SIZE / 2),
    Math.ceil(gl.canvas.height / CELL_SIZE / 2)
  );
  const width = universe.width();
  const height = universe.height();

  let points = [];

  for (let i = 0; i < width; i++) {
    for (let j = 0; j < height; j++) {
      points.push([i * CELL_SIZE * 2, j * CELL_SIZE * 2]);
    }
  }

  const cellsPtr = universe.cells();
  // Webassembly memory (only 0's and 1's)
  const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);

  const arrays = {
    position: {
      data: new Float32Array(points.flat()),
      size: 2
    },
    color: {
      data: cells,
      size: 1,
      type: gl.UNSIGNED_BYTE
    }
  };

  const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

  const uniforms = {
    u_resolution: [gl.canvas.width, gl.canvas.height],
    u_point_width: CELL_SIZE
  };

  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.useProgram(programInfo.program);

  twgl.setUniforms(programInfo, uniforms);

  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);

  twgl.drawBufferInfo(gl, bufferInfo, gl.POINTS);

  function renderLoop(time) {
    twgl.resizeCanvasToDisplaySize(gl.canvas, window.devicePixelRatio);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    universe.tick();
    const cellsPtr = universe.cells();
    const cells = new Uint8Array(
      memory.buffer,
      cellsPtr,
      width * height
    );

    const uniforms = {
      u_resolution: [gl.canvas.width, gl.canvas.height],
      u_point_width: CELL_SIZE
    };

    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.useProgram(programInfo.program);
    twgl.setUniforms(programInfo, uniforms);

    twgl.setAttribInfoBufferFromArray(gl, bufferInfo.attribs.a_color, {
      data: cells,
      size: 1,
      type: gl.UNSIGNED_BYTE
    }); // Dynamically update buffer with new colors

    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);

    twgl.drawBufferInfo(gl, bufferInfo, gl.POINTS);
    requestAnimationFrame(renderLoop);
  }

  requestAnimationFrame(renderLoop);
};

У меня нет проблемы, если я вручную конвертирую cells в Float32Array. Я что-то не так делаю?

Вот упрощенная версия приведенного выше кода (на экране должны быть зеленые точки, но их нет):

https://codesandbox.io/s/silly-jackson-ut3wm

Ответы [ 2 ]

0 голосов
/ 14 марта 2020

Используемая вами библиотека не всегда имеет значения по умолчанию undefined. Установка normalized в true по некоторому пути для типов массивов Uint8Array и Int8Array.

Нормализация данных буфера атрибута

Аргумент нормализации, если true in WebGLRenderingContext.vertexAttribPointer нормализует целое число в диапазоне единиц измерения. Для подписанных от -1 до 1 и для беззнаковых от 0 до 1.

В результате атрибут color устанавливается на 1 / 255, когда вы используете массив типа Uint8Array и don t define the value of normalized`

В вершинном шейдере вы можете уменьшить значение до 1.

Пример вершинного шейдера

attribute float color;

//... code

varying vec4 colorOut;
void main() {

    // ... code

    colorOut = vec4(0, color * 255.0, 0, 1);  // scale the color by 255
}

Пример

Показано, как изменяется normalized данные атрибута. Красный не нормализован, а зеленый. Обратите внимание, что здесь не используется никакая библиотека.

const GL_OPTIONS = {alpha: false, depth: false, premultpliedAlpha: false, preserveDrawingBufer: true};
const CELL_SIZE = 10, GRID_SIZE = 32, GRID_PX_WIDTH = GRID_SIZE * CELL_SIZE, CELLS = GRID_SIZE ** 2;
const GL_SETUP = {
    get context() { return  canvas.getContext("webgl", GL_OPTIONS) },
    get locations() { return ["A_pos", "A_green", "A_red", "U_res"] },  
    get vertex() { return `
attribute vec2 pos;
attribute float green;
attribute float red;
uniform vec2 res;
varying vec4 colorV;
void main() {
    gl_PointSize = ${(CELL_SIZE - 2).toFixed(1)};
    gl_Position = vec4(pos / res, 0, 1);
    colorV = vec4(red, green * 255.0, 0, 1);
}`;
    },
    get fragment() { return `
precision highp float;
varying vec4 colorV;
void main() {
    gl_FragColor = colorV;
}`;
    },
    get buffers() {
        return {
            pos: { buffer: GL_SETUP.grid },
            red: { size: 1, type: gl.UNSIGNED_BYTE, normalize: false, buffer: GL_SETUP.colorsRed },
            green: { size: 1, type: gl.UNSIGNED_BYTE, normalize: true, buffer: GL_SETUP.colorsGreen }
        };
    },
    get grid() {
        var i = CELLS, buf = new Float32Array(i * 2), idx;
        while (i--) {
            idx = i << 1;
            buf[idx++]  = (i % GRID_SIZE) * CELL_SIZE - GRID_PX_WIDTH / 2;
            buf[idx] = (i / GRID_SIZE | 0) * CELL_SIZE - GRID_PX_WIDTH / 2;
        }
        return buf;
    },
    get colorsRed() {
        var i = CELLS, buf = new Uint8Array(i);
        while(i--) { buf[i] = Math.random() < 0.5 && i < CELLS / 2 ? 1 : 0 }
        return buf; 
    },    
    get colorsGreen() {
        var i = CELLS, buf = new Uint8Array(i);
        while(i--) { buf[i] = Math.random() < 0.5 && i >= CELLS / 2 ? 1 : 0 }
        return buf; 
    },    
};

const gl = GL_SETUP.context;
const shader = initShader(gl, GL_SETUP);
const W = canvas.width = innerWidth, H = canvas.height = innerHeight;
gl.viewport(0, 0, W, H);
gl.uniform2fv(shader.locations.res, new Float32Array([W / 2 , -H / 2]));
gl.drawArrays(gl.POINTS, 0, CELLS);

function compileAndAttach(gl, program, src, type = gl.VERTEX_SHADER) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, src);
    gl.compileShader(shader);   
    gl.attachShader(program, shader);
}
function createShader(gl, settings) {
    const locations = {}, program = gl.createProgram();
    compileAndAttach(gl, program, settings.vertex);
    compileAndAttach(gl, program, settings.fragment, gl.FRAGMENT_SHADER);
    gl.linkProgram(program);
    gl.useProgram(program);
    for(const desc of settings.locations) {
        const [type, name] = desc.split("_");
        locations[name] = gl[`get${type==="A" ? "Attrib" : "Uniform"}Location`](program, name);
    }
    return {program, locations, gl};
}
function initShader(gl, settings) {
    const shader = createShader(gl, settings);
    for (const [name, data] of Object.entries(settings.buffers)) {
        const {use = gl.STATIC_DRAW, type = gl.FLOAT, buffer, size = 2, normalize = false} = data;
        gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
        gl.bufferData(gl.ARRAY_BUFFER, buffer, use);
        gl.enableVertexAttribArray(shader.locations[name]);
        gl.vertexAttribPointer(shader.locations[name], size, type, normalize, 0, 0);
    }
    return shader;
}
body { padding: 0px }
canvas {
  position: absolute;
  top: 0px;
  left: 0px;
}
<canvas id="canvas"></canvas>
0 голосов
/ 13 марта 2020

Если вы на самом деле используете twgl, то точно так же, как он угадывает size = 3 для позиции, 4 для цветов, 2 для texcoords, проблема, которую twgl предполагает использовать normalize: true, и вам нужно сказать это normalize: false

С документы

normalize логическое значение --- нормализация для vertexAttribPointer. По умолчанию true, если тип Int8Array или Uint8Array, в противном случае false.

Итак,

  const arrays = {
    position: {
      data: new Float32Array(points.flat()),
      size: 2
    },
    color: {
      data: cells,
      size: 1,
      type: gl.UNSIGNED_BYTE,
      normalize: false,       // added <------------------------
    }
  };

Кстати: если cells - это Uint8Array тогда вам не нужно type: gl.UNSIGNED_BYTE. twgl установит тип на gl.UNSIGNED_BYTE на основании того факта, что данные Uint8Array.

...