渲染无符号 8 位纹理特性
Rendering unsigned 8-bit textures specificities
我一直在尝试使用基于整数的纹理(有关上下文,请参阅此问题),但我无法设法从基于浮点数的纹理 gl.RGBA/gl.RGBA
过渡到 gl.RGBA8UI/gl.RGBA_INTEGER
。
我已经将提到的 sampler2D
替换为 usampler2D
,将 vec4
替换为 uvec4
(对于 fragColor
),重写了纹理格式,但没有绘制。我也不能使用 glClear,显示错误:glClear: can't be called on integer buffers
。使用基于整数的纹理时,是否有任何需要注意的细节?
编辑:它似乎在 Google Chrome 上工作,而不是在 Firefox 上工作?
const baseImage = new Image();
baseImage.src = 'https://i.imgur.com/O6aW2Tg.png';
baseImage.crossOrigin = 'anonymous';
baseImage.onload = function() {
render(baseImage);
};
const vertexShaderSource = `#version 300 es
precision mediump float;
in vec2 position;
out vec2 textureCoordinate;
void main() {
textureCoordinate = vec2(1.0 - position.x, 1.0 - position.y);
gl_Position = vec4((1.0 - 2.0 * position), 0, 1);
}`;
const fragmentShaderSource = `#version 300 es
precision mediump float;
precision highp usampler2D;
uniform usampler2D inputTexture;
in vec2 textureCoordinate;
out uvec4 fragColor;
void main() {
fragColor = texture(inputTexture, textureCoordinate);
}`;
function render(image) {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
return;
}
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]),
gl.STATIC_DRAW
);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const program = webglUtils.createProgramFromSources(gl, [
vertexShaderSource,
fragmentShaderSource,
]);
const positionAttributeLocation = gl.getAttribLocation(
program,
'position'
);
const inputTextureUniformLocation = gl.getUniformLocation(
program,
'inputTexture'
);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
positionAttributeLocation,
2,
gl.FLOAT,
false,
0,
0
);
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const rawTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, image);
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const outputTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, outputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, image.width,
image.height,
0, gl.RGBA_INTEGER, 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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
outputTexture,
0
);
gl.viewport(0, 0, image.width, image.height);
gl.clearColor(0, 0, 0, 1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1i(inputTextureUniformLocation, 0);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindVertexArray(null);
const pixels = new Uint8Array(4 * image.width * image.height);
gl.readPixels(
0,
0,
image.width,
image.height,
gl.RGBA_INTEGER,
gl.UNSIGNED_BYTE,
pixels
);
console.log(pixels);
}
<canvas id="canvas"></canvas>
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>
您的代码呈现得很好。它在我们可以在 JavaScript 控制台中看到的 readPixels 上失败,firefox 打印错误
Error: WebGL warning: readPixels: Incompatible format or type.
不幸的是part of the spec。
该规范列出了您可以创建的所有纹理内部格式,以及您可以将哪些 format/type 数据组合传递给 texImage2D
以将数据上传到它们中的每一个。但是,相反,您可以使用哪些 format/type 组合来读取像素并不那么明确。
这就是它所说的,第 4.3.2 节
Only two combinations of format and type are accepted in most cases. The first varies depending on the format of the currently bound rendering surface. For normalized fixed-point rendering surfaces, the combination format RGBA
and type
UNSIGNED_BYTE
is accepted. For signed integer rendering surfaces, the combination format RGBA_INTEGER
and type INT
is accepted. For unsigned integer rendering surfaces, the combination format RGBA_INTEGER
and type UNSIGNED_INT
is accepted.
The second is an implementation-chosen format from among those defined
in table 3.2, excluding formats DEPTH_COMPONENT
and DEPTH_STENCIL
. The
values of format and type for this format may be determined by calling GetIntegerv with the symbolic constants IMPLEMENTATION_COLOR_READ_FORMAT
and IMPLEMENTATION_COLOR_READ_TYPE
, respectively. ... The implementation-chosen format may vary depending on the format of the selected read buffer of the currently bound read framebuffer.
Additionally, when the internal format of the rendering surface is RGB10_A2,
a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
is accepted.
Table 3.2,您可以在页面上看到 on this page 4th table 的版本,列出大量 format/type 组合,注意规范很重要不规定哪些 format/type 组合是有效的。换句话说,它 而不是 说从 table 3.2 中选择一个 format/type 组合,它对应于当前的内部格式。相反,它只是说 table 中的任何 format/type 组合都是有效的。是的,你没有看错。根据规范,您可以上传 RGBA/INT 纹理,并且实现可能会决定您的第二种格式是 R/FLOAT ¯\_(ツ)_/¯
这里有一些代码可以打印出 RGBA8UI 纹理的第二个允许的 readPixels format/type 组合
function main() {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
return alert('need webgl2');
}
const outputTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, outputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, 4, 4,
0, gl.RGBA_INTEGER, 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 framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
outputTexture,
0
);
console.log(
`format/type: ${
glEnumToString(gl, gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT))
}/${
glEnumToString(gl, gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE))
}`);
}
main();
function glEnumToString(gl, value) {
for (const key in gl) {
if (gl[key] === value) {
return key;
}
}
return `0x${value.toFixed(16)}`;
}
<canvas id="canvas"></canvas>
如果我 运行 上面的代码 Chrome 说
format/type: RGBA_INTEGER/UNSIGNED_BYTE
但是 firefox 说
format/type: RGBA_INTEGER/UNSIGNED_INT
根据规范,两者均有效。
如果您希望它在任何地方都能工作,您需要将数据读取为 RGBA_INTEGER/UNSIGNED_INT
,因为上面规范的第一部分说该格式始终支持无符号整数格式。
更改您的代码以使其在两种浏览器上都能正常工作
const baseImage = new Image();
baseImage.src = 'https://i.imgur.com/O6aW2Tg.png';
baseImage.crossOrigin = 'anonymous';
baseImage.onload = function() {
render(baseImage);
};
const vertexShaderSource = `#version 300 es
precision mediump float;
in vec2 position;
out vec2 textureCoordinate;
void main() {
textureCoordinate = vec2(1.0 - position.x, 1.0 - position.y);
gl_Position = vec4((1.0 - 2.0 * position), 0, 1);
}`;
const fragmentShaderSource = `#version 300 es
precision mediump float;
precision highp usampler2D;
uniform usampler2D inputTexture;
in vec2 textureCoordinate;
out uvec4 fragColor;
void main() {
fragColor = texture(inputTexture, textureCoordinate);
}`;
function render(image) {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
return;
}
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]),
gl.STATIC_DRAW
);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const program = webglUtils.createProgramFromSources(gl, [
vertexShaderSource,
fragmentShaderSource,
]);
const positionAttributeLocation = gl.getAttribLocation(
program,
'position'
);
const inputTextureUniformLocation = gl.getUniformLocation(
program,
'inputTexture'
);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
positionAttributeLocation,
2,
gl.FLOAT,
false,
0,
0
);
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const rawTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, image);
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const outputTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, outputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, image.width,
image.height,
0, gl.RGBA_INTEGER, 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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
outputTexture,
0
);
gl.viewport(0, 0, image.width, image.height);
gl.clearColor(0, 0, 0, 1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1i(inputTextureUniformLocation, 0);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindVertexArray(null);
const pixels = new Uint32Array(4 * image.width * image.height);
gl.readPixels(
0,
0,
image.width,
image.height,
gl.RGBA_INTEGER,
gl.UNSIGNED_INT,
pixels
);
console.log(pixels.slice(0, 40));
}
<canvas id="canvas"></canvas>
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>
我一直在尝试使用基于整数的纹理(有关上下文,请参阅此问题),但我无法设法从基于浮点数的纹理 gl.RGBA/gl.RGBA
过渡到 gl.RGBA8UI/gl.RGBA_INTEGER
。
我已经将提到的 sampler2D
替换为 usampler2D
,将 vec4
替换为 uvec4
(对于 fragColor
),重写了纹理格式,但没有绘制。我也不能使用 glClear,显示错误:glClear: can't be called on integer buffers
。使用基于整数的纹理时,是否有任何需要注意的细节?
编辑:它似乎在 Google Chrome 上工作,而不是在 Firefox 上工作?
const baseImage = new Image();
baseImage.src = 'https://i.imgur.com/O6aW2Tg.png';
baseImage.crossOrigin = 'anonymous';
baseImage.onload = function() {
render(baseImage);
};
const vertexShaderSource = `#version 300 es
precision mediump float;
in vec2 position;
out vec2 textureCoordinate;
void main() {
textureCoordinate = vec2(1.0 - position.x, 1.0 - position.y);
gl_Position = vec4((1.0 - 2.0 * position), 0, 1);
}`;
const fragmentShaderSource = `#version 300 es
precision mediump float;
precision highp usampler2D;
uniform usampler2D inputTexture;
in vec2 textureCoordinate;
out uvec4 fragColor;
void main() {
fragColor = texture(inputTexture, textureCoordinate);
}`;
function render(image) {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
return;
}
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]),
gl.STATIC_DRAW
);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const program = webglUtils.createProgramFromSources(gl, [
vertexShaderSource,
fragmentShaderSource,
]);
const positionAttributeLocation = gl.getAttribLocation(
program,
'position'
);
const inputTextureUniformLocation = gl.getUniformLocation(
program,
'inputTexture'
);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
positionAttributeLocation,
2,
gl.FLOAT,
false,
0,
0
);
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const rawTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, image);
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const outputTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, outputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, image.width,
image.height,
0, gl.RGBA_INTEGER, 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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
outputTexture,
0
);
gl.viewport(0, 0, image.width, image.height);
gl.clearColor(0, 0, 0, 1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1i(inputTextureUniformLocation, 0);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindVertexArray(null);
const pixels = new Uint8Array(4 * image.width * image.height);
gl.readPixels(
0,
0,
image.width,
image.height,
gl.RGBA_INTEGER,
gl.UNSIGNED_BYTE,
pixels
);
console.log(pixels);
}
<canvas id="canvas"></canvas>
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>
您的代码呈现得很好。它在我们可以在 JavaScript 控制台中看到的 readPixels 上失败,firefox 打印错误
Error: WebGL warning: readPixels: Incompatible format or type.
不幸的是part of the spec。
该规范列出了您可以创建的所有纹理内部格式,以及您可以将哪些 format/type 数据组合传递给 texImage2D
以将数据上传到它们中的每一个。但是,相反,您可以使用哪些 format/type 组合来读取像素并不那么明确。
这就是它所说的,第 4.3.2 节
Only two combinations of format and type are accepted in most cases. The first varies depending on the format of the currently bound rendering surface. For normalized fixed-point rendering surfaces, the combination format
RGBA
and typeUNSIGNED_BYTE
is accepted. For signed integer rendering surfaces, the combination formatRGBA_INTEGER
and typeINT
is accepted. For unsigned integer rendering surfaces, the combination formatRGBA_INTEGER
and typeUNSIGNED_INT
is accepted.The second is an implementation-chosen format from among those defined in table 3.2, excluding formats
DEPTH_COMPONENT
andDEPTH_STENCIL
. The values of format and type for this format may be determined by calling GetIntegerv with the symbolic constantsIMPLEMENTATION_COLOR_READ_FORMAT
andIMPLEMENTATION_COLOR_READ_TYPE
, respectively. ... The implementation-chosen format may vary depending on the format of the selected read buffer of the currently bound read framebuffer.Additionally, when the internal format of the rendering surface is RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV is accepted.
Table 3.2,您可以在页面上看到 on this page 4th table 的版本,列出大量 format/type 组合,注意规范很重要不规定哪些 format/type 组合是有效的。换句话说,它 而不是 说从 table 3.2 中选择一个 format/type 组合,它对应于当前的内部格式。相反,它只是说 table 中的任何 format/type 组合都是有效的。是的,你没有看错。根据规范,您可以上传 RGBA/INT 纹理,并且实现可能会决定您的第二种格式是 R/FLOAT ¯\_(ツ)_/¯
这里有一些代码可以打印出 RGBA8UI 纹理的第二个允许的 readPixels format/type 组合
function main() {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
return alert('need webgl2');
}
const outputTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, outputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, 4, 4,
0, gl.RGBA_INTEGER, 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 framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
outputTexture,
0
);
console.log(
`format/type: ${
glEnumToString(gl, gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT))
}/${
glEnumToString(gl, gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE))
}`);
}
main();
function glEnumToString(gl, value) {
for (const key in gl) {
if (gl[key] === value) {
return key;
}
}
return `0x${value.toFixed(16)}`;
}
<canvas id="canvas"></canvas>
如果我 运行 上面的代码 Chrome 说
format/type: RGBA_INTEGER/UNSIGNED_BYTE
但是 firefox 说
format/type: RGBA_INTEGER/UNSIGNED_INT
根据规范,两者均有效。
如果您希望它在任何地方都能工作,您需要将数据读取为 RGBA_INTEGER/UNSIGNED_INT
,因为上面规范的第一部分说该格式始终支持无符号整数格式。
更改您的代码以使其在两种浏览器上都能正常工作
const baseImage = new Image();
baseImage.src = 'https://i.imgur.com/O6aW2Tg.png';
baseImage.crossOrigin = 'anonymous';
baseImage.onload = function() {
render(baseImage);
};
const vertexShaderSource = `#version 300 es
precision mediump float;
in vec2 position;
out vec2 textureCoordinate;
void main() {
textureCoordinate = vec2(1.0 - position.x, 1.0 - position.y);
gl_Position = vec4((1.0 - 2.0 * position), 0, 1);
}`;
const fragmentShaderSource = `#version 300 es
precision mediump float;
precision highp usampler2D;
uniform usampler2D inputTexture;
in vec2 textureCoordinate;
out uvec4 fragColor;
void main() {
fragColor = texture(inputTexture, textureCoordinate);
}`;
function render(image) {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
return;
}
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]),
gl.STATIC_DRAW
);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const program = webglUtils.createProgramFromSources(gl, [
vertexShaderSource,
fragmentShaderSource,
]);
const positionAttributeLocation = gl.getAttribLocation(
program,
'position'
);
const inputTextureUniformLocation = gl.getUniformLocation(
program,
'inputTexture'
);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
positionAttributeLocation,
2,
gl.FLOAT,
false,
0,
0
);
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const rawTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, image);
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const outputTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, outputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, image.width,
image.height,
0, gl.RGBA_INTEGER, 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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
outputTexture,
0
);
gl.viewport(0, 0, image.width, image.height);
gl.clearColor(0, 0, 0, 1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1i(inputTextureUniformLocation, 0);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rawTexture);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindVertexArray(null);
const pixels = new Uint32Array(4 * image.width * image.height);
gl.readPixels(
0,
0,
image.width,
image.height,
gl.RGBA_INTEGER,
gl.UNSIGNED_INT,
pixels
);
console.log(pixels.slice(0, 40));
}
<canvas id="canvas"></canvas>
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>