Если я использую webgl2 для рендеринга чего-либо (например, треугольника) в текстуру RGBA UNSIGNED_BYTE, а затем визуализирую эту текстуру на холст, все работает нормально. Но у меня возникли проблемы с работой моего примера при рендеринге текстуры FLOAT RGBA32F на холсте. Вместо этого я просто получаю сплошной черный ящик, но без сообщения об ошибке. Есть идеи, как заставить работать пример с плавающей запятой?
Не работает плавающая текстура jsfiddle
В этом примере используется int-текстура, которая работает должным образом (в качестве текстуры выводится фиолетовый треугольник с белым фоном, который накладывается на более крупный серый фон).
"use strict";
function main() {
// Get the WebGL context
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl2");
const textureWidth = 100;
const textureHeight = 100;
// Render purple triangle to texture
{
const vertexShaderSource = `#version 300 es
in vec4 a_position;
void main() {
gl_Position = a_position;
}
`;
const fragmentShaderSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(1,0,1, 1);
}
`;
// create/compile shaders
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = createProgram(gl, vertexShader, fragmentShader);
// set a_position attribute
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
-1, -1,
0, 1,
1, 0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);
// Tell the attribute how to get data out of positionBuffer
const size = 2; // 2 components per iteration
let type = gl.FLOAT; // the data is 32bit floats
const normalize = false; // don't normalize the data
const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
let offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
gl.useProgram(program);
// set up the target texture
const targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);
const level = 0;
const internalFormat = gl.RGBA;
const border = 0;
const format = gl.RGBA;
type = gl.UNSIGNED_BYTE;
const data = null;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
textureWidth, textureHeight, border,
format, type, data);
// set the filtering so we don't need mips
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
// Create and bind the framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
// attach the texture as the first color attachment
const attachmentPoint = gl.COLOR_ATTACHMENT0;
gl.framebufferTexture2D(
gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level);
gl.viewport(0, 0, textureWidth, textureHeight);
// Clear the canvas
gl.clearColor(1,1,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
const primitiveType = gl.TRIANGLES;
offset = 0;
const count = 3;
gl.drawArrays(primitiveType, offset, count);
}
// Now draw the texture to the canvas
{
const vertexShaderSource = `#version 300 es
in vec2 a_position;
in vec2 a_texCoord;
out vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0, 1);
v_texCoord = a_texCoord;
}
`;
const fragmentShaderSource = `#version 300 es
precision highp float;
uniform sampler2D u_image;
in vec2 v_texCoord;
out vec4 outColor;
void main() {
outColor = texture(u_image , v_texCoord);
}
`;
// render to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
const program = webglUtils.createProgramFromSources(gl,
[vertexShaderSource, fragmentShaderSource]);
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
const texCoordAttributeLocation = gl.getAttribLocation(program, "a_texCoord");
// lookup uniforms
const imageLocation = gl.getUniformLocation(program, "u_image");
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const positionBuffer = gl.createBuffer();
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
let size = 2; // 2 components per iteration
let type = gl.FLOAT; // the data is 32bit floats
let normalize = false; // don't normalize the data
let stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
let offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
0, 1,
1, 0,
1, 0,
1, 1,
0, 1
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordAttributeLocation);
size = 2; // 2 components per iteration
type = gl.FLOAT; // the data is 32bit floats
normalize = false; // don't normalize the data
stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texCoordAttributeLocation, size, type, normalize, stride, offset);
window.gl = gl
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas to grey
gl.clearColor(0.5,0.5,0.5,1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1i(imageLocation, 0);
// Bind the position buffer so gl.bufferData that will be called
// in setRectangle puts data in the position buffer
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle the same size as the image.
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, -0.5,
0.5, 0.5,
-0.5, 0.5
]), gl.STATIC_DRAW);
// Draw the rectangle.
const primitiveType = gl.TRIANGLES;
offset = 0;
const count = 6;
gl.drawArrays(primitiveType, offset, count);
}
}
main();
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.info(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return undefined;
}
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.info(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return undefined;
}
И это очень похожий код с изменениями, позволяющими включить текстуру float32. В консоли ошибок нет, но появляется просто черный квадрат на сером фоне. Никакого фиолетового треугольника :(
"use strict";
function main() {
// Get the WebGL context
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl2");
const textureWidth = 100;
const textureHeight = 100;
// Render purple triangle to texture
{
const vertexShaderSource = `#version 300 es
in vec4 a_position;
void main() {
gl_Position = a_position;
}
`;
const fragmentShaderSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(1,0,1, 1);
}
`;
// create/compile shaders
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = createProgram(gl, vertexShader, fragmentShader);
// set a_position attribute
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
-1, -1,
0, 1,
1, 0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);
// Tell the attribute how to get data out of positionBuffer
const size = 2; // 2 components per iteration
let type = gl.FLOAT; // the data is 32bit floats
const normalize = false; // don't normalize the data
const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
let offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
gl.useProgram(program);
// set up the target texture
// enable float texture rendering
const ext = gl.getExtension("EXT_color_buffer_float");
const targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);
const level = 0;
const internalFormat = gl.RGBA32F;
const border = 0;
const format = gl.RGBA;
type = gl.FLOAT;
const data = null;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
textureWidth, textureHeight, border,
format, type, data);
// set the filtering so we don't need mips
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
// Create and bind the framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
// attach the texture as the first color attachment
const attachmentPoint = gl.COLOR_ATTACHMENT0;
gl.framebufferTexture2D(
gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level);
gl.viewport(0, 0, textureWidth, textureHeight);
// Clear the canvas
gl.clearColor(1,1,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
const primitiveType = gl.TRIANGLES;
offset = 0;
const count = 3;
gl.drawArrays(primitiveType, offset, count);
}
// Now draw the texture to the canvas
{
const vertexShaderSource = `#version 300 es
in vec2 a_position;
in vec2 a_texCoord;
out vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0, 1);
v_texCoord = a_texCoord;
}
`;
const fragmentShaderSource = `#version 300 es
precision highp float;
uniform sampler2D u_image;
in vec2 v_texCoord;
out vec4 outColor;
void main() {
outColor = texture(u_image , v_texCoord);
}
`;
// render to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
const program = webglUtils.createProgramFromSources(gl,
[vertexShaderSource, fragmentShaderSource]);
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
const texCoordAttributeLocation = gl.getAttribLocation(program, "a_texCoord");
// lookup uniforms
const imageLocation = gl.getUniformLocation(program, "u_image");
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const positionBuffer = gl.createBuffer();
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
let size = 2; // 2 components per iteration
let type = gl.FLOAT; // the data is 32bit floats
let normalize = false; // don't normalize the data
let stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
let offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
0, 1,
1, 0,
1, 0,
1, 1,
0, 1
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordAttributeLocation);
size = 2; // 2 components per iteration
type = gl.FLOAT; // the data is 32bit floats
normalize = false; // don't normalize the data
stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texCoordAttributeLocation, size, type, normalize, stride, offset);
window.gl = gl
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas to grey
gl.clearColor(0.5,0.5,0.5,1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1i(imageLocation, 0);
// Bind the position buffer so gl.bufferData that will be called
// in setRectangle puts data in the position buffer
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle the same size as the image.
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, -0.5,
0.5, 0.5,
-0.5, 0.5
]), gl.STATIC_DRAW);
// Draw the rectangle.
const primitiveType = gl.TRIANGLES;
offset = 0;
const count = 6;
gl.drawArrays(primitiveType, offset, count);
}
}
main();
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.info(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return undefined;
}
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.info(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return undefined;
}
Согласно документации , внутренний формат RGBA32F
не фильтруется. Поэтому неправильно устанавливать TEXTURE_MIN_FILTER
на LINEAR
, и вам следует изменить его на NEAREST
. Однако этого недостаточно, поскольку значение по умолчанию для TEXTURE_MAG_FILTER
равно LINEAR
, поэтому вам также придется изменить его на NEAREST
. Так что измени
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
к
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
Я не уверен, почему это не ошибка в консоли.
Отлично, это решает проблему, спасибо. Странно, что в консоли нет ошибок. Я еще немного прочитал о фильтрации после того, как вы определили ее как проблему: если вы действительно хотите использовать gl.LINEAR с текстурами с плавающей запятой, вы можете добавить другое расширение, которое включает это с помощью
gl.getExtension("OES_texture_float_linear");