Почему мой вершинный шейдер игнорирует данные Uint8Array, но не данные Float32Array?
Мой вершинный шейдер имеет следующий атрибут:
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
. Я делаю что-то неправильно?
Вот живая упрощенная версия приведенного выше кода (на экране должны быть зеленые точки, но их нет):
2 ответа
Если вы действительно используете 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
.
Библиотека, которую вы используете, не всегда по умолчанию undefined
свойства. Настройкаnormalized
к true
по некоторому пути для Uint8Array
а также Int8Array
типы массивов.
Нормализация данных атрибутивного буфера
Аргумент нормализации, если true
в WebGLRenderingContext.vertexAttribPointer
нормализует целое число в пределах диапазона единиц. Для подписанных от -1 до 1 и для беззнаковых от 0 до 1.
Это приводит к color
атрибут установлен на 1 / 255
когда вы используете массив типа Uint8Array
и донt define the value of
нормализованный
Вы можете снова масштабировать значение до 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>