我正在尝试将一些非纹理值传递给纹理中的像素着色器,并且我遇到一个奇怪的问题,当纹理的alpha值为零时sampler2d
返回vec4(0.0)
,无论其他3个字节的值如何。
这不是预乘的alpha事物,或者是混合物,当alpha的字节值为1到255时,它不会发生,只有零。
如果你运行下面的代码,你会在小的2d画布中看到2x2纹理被渲染到大型3D画布中。所有4个像素都具有r,g,b = 255.并且所有4个像素具有不同的值。纹理的左上角像素的alpha值为零。
像素着色器始终设置gl_FragColor.a = 1.0
。
我不相信这是一个预乘alpha的原因是,如果它是,那么肯定所有3个像素将是不同的灰色阴影?
谁能告诉我为什么会这样?
const cvs = document.getElementById("cvs"),
{
width: W,
height: H
} = cvs.getBoundingClientRect();
cvs.width = W;
cvs.height = H;
const gl = cvs.getContext("experimental-webgl", {
premultipliedAlpha: false
}),
VERTEX_SHADER = `attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main() {
gl_Position = a_Position;
v_TexCoord = a_TexCoord;
}`,
FRAGMENT_SHADER = `precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
gl_FragColor.rgb = texture2D(u_Sampler, v_TexCoord).rgb;
gl_FragColor.a = 1.0;
}`,
vshader = gl.createShader(gl.VERTEX_SHADER),
fshader = gl.createShader(gl.FRAGMENT_SHADER),
program = gl.createProgram();
gl.shaderSource(vshader, VERTEX_SHADER);
gl.shaderSource(fshader, FRAGMENT_SHADER);
gl.compileShader(vshader);
gl.compileShader(fshader);
gl.attachShader(program, vshader);
gl.attachShader(program, fshader);
gl.linkProgram(program);
gl.useProgram(program);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
const farr = new Float32Array([-1, 1, 0, 1, -1, -1, 0, 0,
1, 1, 1, 1,
1, -1, 1, 0
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, farr, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(program, "a_Position"),
a_TexCoord = gl.getAttribLocation(program, "a_TexCoord"),
fsize = farr.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 4 * fsize, 0);
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 4 * fsize, 2 * fsize);
gl.enableVertexAttribArray(a_Position);
gl.enableVertexAttribArray(a_TexCoord);
var image = document.getElementById("img"),
context = image.getContext("2d"),
imageData = context.getImageData(0, 0, 2, 2),
pixels = imageData.data;
for (var i = 0; i < pixels.length; i++) {
pixels[i] = 255;
}
pixels[0 * 4 + 3] = 0;
pixels[1 * 4 + 3] = 1;
pixels[2 * 4 + 3] = 128;
context.putImageData(imageData, 0, 0, 0, 0, 2, 2);
const texture = gl.createTexture(),
u_Sampler = gl.getUniformLocation(program, "u_Sampler");
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.uniform1i(u_Sampler, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
html,
body,
canvas {
padding: 0;
margin: 0;
}
canvas {
border: 1px solid red;
display: block;
margin: 5px;
}
body {
background: black;
display: block;
position: absolute;
width: 100%;
height: 100%;
}
#cvs {
width: 100px;
height: 100px;
}
<canvas id="cvs"></canvas>
<canvas id="img" width="2" height="2"></canvas>
答案 0 :(得分:2)
@pleup说的是什么。 Canvas 2d值始终写入画布预乘。这意味着您调用putImageData
时数据乘以alpha并且数据丢失。
const ctx = document.createElement("canvas").getContext("2d");
const imgData = ctx.createImageData(2, 2);
const data = imgData.data;
data[ 0] = 255; data[ 3] = 255;
data[ 4] = 255; data[ 7] = 192;
data[ 8] = 255; data[11] = 64;
data[12] = 255; data[15] = 0;
ctx.putImageData(imgData, 0, 0);
const newImgData = ctx.getImageData(0, 0, 2, 2);
const newData = newImgData.data;
console.log(newData[ 0], newData[ 3]);
console.log(newData[ 4], newData[ 7]);
console.log(newData[ 8], newData[11]);
console.log(newData[12], newData[15]);
&#13;
使用255作为颜色部分隐藏了问题,因为在您致电putImageData
后,2d画布中的内容是
red = 255 alpha = 255
red = 192 alpha = 192
red = 64 alpha = 64
red = 0 alpha = 0
Unpremultiplying(上传到WebGL时)除了0之外的所有值都会返回255秒
red alpha result
red = 255 alpha = 255 = 255 * 255 / 255 = 255
red = 192 alpha = 192 = 192 * 255 / 192 = 255
red = 64 alpha = 64 = 64 * 255 / 64 = 255
red = 0 alpha = 0 = 0 * 0 = 0
如果颜色值为20且不同的alphas
red = 20 * 10 / 255 = 1 alpha = 10
red = 20 * 7 / 255 = 1 alpha = 7
red = 20 * 4 / 255 = 0 alpha = 4
red = 20 * 0 / 255 = 0 alpha = 0
然后取而代之的是
1 * 255 / 10 = 26 not even close to what we put in
1 * 255 / 7 = 36 not even close to what we put in
0 * 255 / 4 = 0
0 * 0 = 0
指出它是多么有损。
const ctx = document.createElement("canvas").getContext("2d");
const imgData = ctx.createImageData(2, 2);
const data = imgData.data;
data[ 0] = 20; data[ 3] = 10;
data[ 4] = 20; data[ 7] = 7;
data[ 8] = 20; data[11] = 4;
data[12] = 20; data[15] = 0;
ctx.putImageData(imgData, 0, 0);
const newImgData = ctx.getImageData(0, 0, 2, 2);
const newData = newImgData.data;
console.log(newData[ 0], newData[ 3]);
console.log(newData[ 4], newData[ 7]);
console.log(newData[ 8], newData[11]);
console.log(newData[12], newData[15]);
&#13;
如果您真的想在WebGL中手动将数据放入纹理中,您应该只使用typedArray
const width = 2;
const height = 2;
const data = new Uint8Array(width * height * 4);
data[ 0] = 255; data[ 3] = 255;
data[ 4] = 255; data[ 7] = 192;
data[ 8] = 255; data[11] = 64;
data[12] = 255; data[15] = 0;
const level = 0;
const internalFormat = gl.RGBA;
const border = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
format, type, data);
如果您想要阅读该数据而不是通过2d画布,请致电gl.readPixels
const newData = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, format, type);
const gl = document.querySelector("canvas").getContext("webgl", {
premultipliedAlpha: false,
});
const VERTEX_SHADER = `
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main() {
gl_Position = a_Position;
v_TexCoord = a_TexCoord;
}
`;
const FRAGMENT_SHADER = `
precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}
`;
const vshader = gl.createShader(gl.VERTEX_SHADER);
const fshader = gl.createShader(gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.shaderSource(vshader, VERTEX_SHADER);
gl.shaderSource(fshader, FRAGMENT_SHADER);
gl.compileShader(vshader);
gl.compileShader(fshader);
gl.attachShader(program, vshader);
gl.attachShader(program, fshader);
gl.linkProgram(program);
gl.useProgram(program);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
const farr = new Float32Array([
-1, 1, 0, 1,
-1, -1, 0, 0,
1, 1, 1, 1,
1, -1, 1, 0
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, farr, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(program, "a_Position");
const a_TexCoord = gl.getAttribLocation(program, "a_TexCoord");
const fsize = farr.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 4 * fsize, 0);
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 4 * fsize, 2 * fsize);
gl.enableVertexAttribArray(a_Position);
gl.enableVertexAttribArray(a_TexCoord);
const pixels = new Uint8Array(2 * 2 * 4);
pixels.fill(255);
pixels[0 * 4 + 3] = 0;
pixels[1 * 4 + 3] = 1;
pixels[2 * 4 + 3] = 128;
const texture = gl.createTexture();
const u_Sampler = gl.getUniformLocation(program, "u_Sampler");
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
&#13;
canvas { background: red; }
&#13;
<canvas></canvas>
&#13;