WebGL2 渲染到具有 MSAA 抗锯齿和透明背景的纹理
WebGL2 render to texture with MSAA anti-aliasing and transparent background
我正在尝试渲染一组在 WebGL2 中启用多样本抗锯齿 MSAA 的三角形。
因此,我正在使用多采样渲染缓冲区设置渲染管道
渲染到目标纹理。抗锯齿似乎有效,但是如果我尝试渲染
场景到透明渲染缓冲区,抗锯齿似乎
逐渐融入不透明的背景颜色,尽管它是完全透明的。
在下面的示例图像中,绘制了一组绿色 rgb(0,1,0,1) 三角形:首先将背景透明颜色设置为 gl.clearColor(0, 0, 0, 0) - 第二个清晰的颜色设置为 gl.clearColor(1, 0, 0, 0) -(生成的纹理在白色背景上混合以显示结果)。
如何将场景渲染为透明纹理,抗锯齿从 rgba(0,0,255,1) 逐渐变为 rgba(0,0,0,0)?
//initialization code
gl.frameBufferAA = gl.createFramebuffer();
//render code
let renderBufferAA = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderBufferAA);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, texDst.width, texDst.height);
//attach renderBufferAA to frameBufferRenderBuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.frameBufferAA);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderBufferAA);
gl.clearColor(0, 0, 0, 0); //<--- transparent color affects anti-aliasing
gl.colorMask(true, true, true, true);
gl.clear(gl.COLOR_BUFFER_BIT);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
//blit renderBuffe
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, gl.frameBufferAA);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, gl.frameBuffer1);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texDst, 0);
gl.blitFramebuffer(
0, 0, texDst.width, texDst.height,
0, 0, texDst.width, texDst.height,
gl.COLOR_BUFFER_BIT, gl.NEAREST
);
gl.deleteRenderbuffer(renderBufferAA);
更新:
我创建了一个堆栈溢出片段来隔离问题。 fiddle 绘制一个消除锯齿的红色圆圈。由抗锯齿创建的像素逐渐变为绿色,这是多重采样渲染缓冲区的清晰颜色。
该问题似乎与 alpha=false webgl2 上下文的创建参数有关。
(function () {
'use strict';
var canvas = document.createElement('canvas');
canvas.width = Math.min(window.innerWidth, window.innerHeight);
canvas.height = canvas.width;
document.body.appendChild(canvas);
var gl = canvas.getContext( 'webgl2', { antialias: false, alpha: false } );
var isWebGL2 = !!gl;
if(!isWebGL2) {
document.getElementById('info').innerHTML = 'WebGL 2 is not available. See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
return;
}
// -- Init program
var PROGRAM = {
TEXTURE: 0,
SPLASH: 1,
MAX: 2
};
var programs = [
createProgram(gl, getShaderSource('vs-render'), getShaderSource('fs-render')),
createProgram(gl, getShaderSource('vs-splash'), getShaderSource('fs-splash'))
];
var mvpLocationTexture = gl.getUniformLocation(programs[PROGRAM.TEXTURE], 'MVP');
var mvpLocation = gl.getUniformLocation(programs[PROGRAM.SPLASH], 'MVP');
var diffuseLocation = gl.getUniformLocation(programs[PROGRAM.SPLASH], 'diffuse');
// -- Init primitive data
var vertexCount = 18;
var data = new Float32Array(vertexCount * 2);
var angle;
var radius = 0.1;
for(var i = 0; i < vertexCount; i++ )
{
angle = Math.PI * 2 * i / vertexCount;
data[2 * i] = radius * Math.sin(angle);
data[2 * i + 1] = radius * Math.cos(angle);
}
// -- Init buffers
var vertexDataBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var positions = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0
]);
var vertexPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var texCoords = new Float32Array([
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,
1.0, 0.0,
0.0, 0.0,
0.0, 1.0
]);
var vertexTexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// -- Init Texture
// used for draw framebuffer storage
var FRAMEBUFFER_SIZE = {
x: canvas.width,
y: canvas.height
};
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.bindTexture(gl.TEXTURE_2D, null);
// -- Init Frame Buffers
var FRAMEBUFFER = {
RENDERBUFFER: 0,
COLORBUFFER: 1
};
var framebuffers = [
gl.createFramebuffer(),
gl.createFramebuffer()
];
var colorRenderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.COLORBUFFER]);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// -- Init VertexArray
var vertexArrays = [
gl.createVertexArray(),
gl.createVertexArray()
];
var vertexPosLocation = 0; // set with GLSL layout qualifier
gl.bindVertexArray(vertexArrays[PROGRAM.TEXTURE]);
gl.enableVertexAttribArray(vertexPosLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindVertexArray(null);
gl.bindVertexArray(vertexArrays[PROGRAM.SPLASH]);
gl.enableVertexAttribArray(vertexPosLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var vertexTexLocation = 1; // set with GLSL layout qualifier
gl.enableVertexAttribArray(vertexTexLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
gl.vertexAttribPointer(vertexTexLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindVertexArray(null);
// -- Render
// Pass 1
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.clearBufferfv(gl.COLOR, 0, [0.0, 1.0, 0.0, 0.0]);
gl.useProgram(programs[PROGRAM.TEXTURE]);
gl.bindVertexArray(vertexArrays[PROGRAM.TEXTURE]);
var IDENTITY = mat4.create();
gl.uniformMatrix4fv(mvpLocationTexture, false, IDENTITY);
gl.enable(gl.blend);
gl.blendFunc(gl.SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.LINE_LOOP, 0, vertexCount);
// Blit framebuffers, no Multisample texture 2d in WebGL 2
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, framebuffers[FRAMEBUFFER.COLORBUFFER]);
gl.blitFramebuffer(
0, 0, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y,
0, 0, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y,
gl.COLOR_BUFFER_BIT, gl.NEAREST
);
// Pass 2
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.useProgram(programs[PROGRAM.SPLASH]);
gl.uniform1i(diffuseLocation, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindVertexArray(vertexArrays[PROGRAM.SPLASH]);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var scaleVector3 = vec3.create();
vec3.set(scaleVector3, 8.0, 8.0, 8.0);
var mvp = mat4.create();
mat4.scale(mvp, IDENTITY, scaleVector3);
gl.uniformMatrix4fv(mvpLocation, false, mvp);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// -- Delete WebGL resources
gl.deleteBuffer(vertexPosBuffer);
gl.deleteBuffer(vertexTexBuffer);
gl.deleteTexture(texture);
gl.deleteRenderbuffer(colorRenderbuffer);
gl.deleteFramebuffer(framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.deleteFramebuffer(framebuffers[FRAMEBUFFER.COLORBUFFER]);
gl.deleteVertexArray(vertexArrays[PROGRAM.TEXTURE]);
gl.deleteVertexArray(vertexArrays[PROGRAM.SPLASH]);
gl.deleteProgram(programs[PROGRAM.TEXTURE]);
gl.deleteProgram(programs[PROGRAM.SPLASH]);
})();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block }
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js" integrity="sha256-+09xst+d1zIS41eAvRDCXOf0MH993E4cS40hKBIJj8Q=" crossorigin="anonymous"></script>
<script>
(function () {
'use strict';
window.getShaderSource = function(id) {
return document.getElementById(id).textContent.replace(/^\s+|\s+$/g, '');
};
function createShader(gl, source, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
window.createProgram = function(gl, vertexShaderSource, fragmentShaderSource) {
var program = gl.createProgram();
var vshader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
var fshader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
gl.attachShader(program, vshader);
gl.deleteShader(vshader);
gl.attachShader(program, fshader);
gl.deleteShader(fshader);
gl.linkProgram(program);
var log = gl.getProgramInfoLog(program);
if (log) {
console.log(log);
}
log = gl.getShaderInfoLog(vshader);
if (log) {
console.log(log);
}
log = gl.getShaderInfoLog(fshader);
if (log) {
console.log(log);
}
return program;
};
})();
</script>
<!-- vertex shader -->
<!-- WebGL 2 shaders -->
<script id="vs-render" type="x-shader/x-vertex">
#version 300 es
#define POSITION_LOCATION 0
precision highp float;
precision highp int;
uniform mat4 MVP;
layout(location = POSITION_LOCATION) in vec2 position;
void main()
{
gl_Position = MVP * vec4(position, 0.0, 1.0);
}
</script>
<script id="fs-render" type="x-shader/x-fragment">
#version 300 es
precision highp float;
precision highp int;
out vec4 color;
void main()
{
color = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script id="vs-splash" type="x-shader/x-vertex">
#version 300 es
precision highp float;
precision highp int;
uniform mat4 MVP;
layout(location = 0) in vec2 position;
layout(location = 1) in vec2 texcoord;
out vec2 uv;
void main()
{
uv = texcoord;
gl_Position = MVP * vec4(position, 0.0, 1.0);
}
</script>
<script id="fs-splash" type="x-shader/x-fragment">
#version 300 es
precision highp float;
precision highp int;
uniform sampler2D diffuse;
in vec2 uv;
out vec4 color;
void main()
{
color = texture(diffuse, uv);
}
</script>
<script>
</script>
说问题似乎与'alpha=false creation parameter of the webgl2 context.'有关表明问题是canvas如何与网页本身混合?
A canvas 与页面中 HTML 的其余部分合成(混合),无论 canvas 后面是什么。 canvas 元素本身可以有一个 CSS 背景,页面 <body>
可以有一个背景。 canvas 可以覆盖其他元素。无论它与页面合成。
默认使用blendFunc(ONE, ONE_MINUS_SRC_ALPHA)
有效混合,因此canvas中的颜色需要预乘alpha值。
您可以将 canvas 设置为没有 alpha getContext("webgl", {alpha: false})
在这种情况下,
的 alpha 是有效的 1.0
您还可以告诉浏览器您的像素值是未预乘的 alpha,在这种情况下,它将有效地使用 blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
来合成页面中的 canvas。您使用 getContext("webgl, {premultipliedAlpha: false}
.
执行此操作
我建议您将 canvas 的背景颜色设置为可以清楚显示正在发生的事情的颜色。例如。
canvas {
background-color: #FF0;
background-image:
linear-gradient(45deg, #F0F 25%, transparent 25%),
linear-gradient(-45deg, #F0F 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #F0F 75%),
linear-gradient(-45deg, transparent 75%, #F0F 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
<canvas></canvas>
然后画东西
function main(attribs = {}) {
const canvas = document.createElement('canvas');
canvas.width = 150;
canvas.height = 50;
document.body.appendChild(canvas);
const gl = canvas.getContext('webgl2', attribs);
if (!gl) {
return alert('need webgl2');
}
log('attribs:', JSON.stringify(gl.getContextAttributes()));
const vs = `
attribute vec4 position;
void main() {
gl_Position = position;
}
`;
const fs = `
precision highp float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: [
-0.8, 0.5, 0,
0.8, 0.4, 0,
0.0,-0.5, 0,
],
});
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {color: [0, 1, 0, 1]});
twgl.drawBufferInfo(gl, bufferInfo);
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
main({});
main({premultipliedAlpha: false});
main({antialias: false});
canvas {
image-rendering: pixelated;
background-color: #FF0;
background-image:
linear-gradient(45deg, #F0F 25%, transparent 25%),
linear-gradient(-45deg, #F0F 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #F0F 75%),
linear-gradient(-45deg, transparent 75%, #F0F 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
如果我把它们炸毁,你就会看到区别。您还可以看到它正在从 HTML.
的背景中混合
注意:我没有设置 MSAA 渲染缓冲区,因为通常 canvas 默认情况下本身就是一个 MSAA 渲染缓冲区。这实际上取决于浏览器,但很明显我的 GPU 上的浏览器默认为 canvas.
使用 MSAA
如果不清楚,我的建议是你的结果是你没有设置 {alpha: false}
所以如果你的 canvas 中有任何非 1.0 alpha 值那么您会看到与 canvas.
后面的 HTML 颜色混合的结果
即使是白色背景,我们也会看到一些问题,具体取决于设置
另请注意,您提到清除到 (1, 0, 0, 0)。 1, 0, 0, 0 是默认 "premultipliedAlpha: true" canvas 上的无效颜色。如果预乘颜色,R 不能 > A。
在那种情况下会发生什么是不确定的。过去,firefox 和 chrome 对这些颜色的表现非常不同。
我正在尝试渲染一组在 WebGL2 中启用多样本抗锯齿 MSAA 的三角形。 因此,我正在使用多采样渲染缓冲区设置渲染管道 渲染到目标纹理。抗锯齿似乎有效,但是如果我尝试渲染 场景到透明渲染缓冲区,抗锯齿似乎 逐渐融入不透明的背景颜色,尽管它是完全透明的。
在下面的示例图像中,绘制了一组绿色 rgb(0,1,0,1) 三角形:首先将背景透明颜色设置为 gl.clearColor(0, 0, 0, 0) - 第二个清晰的颜色设置为 gl.clearColor(1, 0, 0, 0) -(生成的纹理在白色背景上混合以显示结果)。
如何将场景渲染为透明纹理,抗锯齿从 rgba(0,0,255,1) 逐渐变为 rgba(0,0,0,0)?
//initialization code
gl.frameBufferAA = gl.createFramebuffer();
//render code
let renderBufferAA = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderBufferAA);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, texDst.width, texDst.height);
//attach renderBufferAA to frameBufferRenderBuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.frameBufferAA);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderBufferAA);
gl.clearColor(0, 0, 0, 0); //<--- transparent color affects anti-aliasing
gl.colorMask(true, true, true, true);
gl.clear(gl.COLOR_BUFFER_BIT);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
//blit renderBuffe
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, gl.frameBufferAA);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, gl.frameBuffer1);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texDst, 0);
gl.blitFramebuffer(
0, 0, texDst.width, texDst.height,
0, 0, texDst.width, texDst.height,
gl.COLOR_BUFFER_BIT, gl.NEAREST
);
gl.deleteRenderbuffer(renderBufferAA);
更新:
我创建了一个堆栈溢出片段来隔离问题。 fiddle 绘制一个消除锯齿的红色圆圈。由抗锯齿创建的像素逐渐变为绿色,这是多重采样渲染缓冲区的清晰颜色。 该问题似乎与 alpha=false webgl2 上下文的创建参数有关。
(function () {
'use strict';
var canvas = document.createElement('canvas');
canvas.width = Math.min(window.innerWidth, window.innerHeight);
canvas.height = canvas.width;
document.body.appendChild(canvas);
var gl = canvas.getContext( 'webgl2', { antialias: false, alpha: false } );
var isWebGL2 = !!gl;
if(!isWebGL2) {
document.getElementById('info').innerHTML = 'WebGL 2 is not available. See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
return;
}
// -- Init program
var PROGRAM = {
TEXTURE: 0,
SPLASH: 1,
MAX: 2
};
var programs = [
createProgram(gl, getShaderSource('vs-render'), getShaderSource('fs-render')),
createProgram(gl, getShaderSource('vs-splash'), getShaderSource('fs-splash'))
];
var mvpLocationTexture = gl.getUniformLocation(programs[PROGRAM.TEXTURE], 'MVP');
var mvpLocation = gl.getUniformLocation(programs[PROGRAM.SPLASH], 'MVP');
var diffuseLocation = gl.getUniformLocation(programs[PROGRAM.SPLASH], 'diffuse');
// -- Init primitive data
var vertexCount = 18;
var data = new Float32Array(vertexCount * 2);
var angle;
var radius = 0.1;
for(var i = 0; i < vertexCount; i++ )
{
angle = Math.PI * 2 * i / vertexCount;
data[2 * i] = radius * Math.sin(angle);
data[2 * i + 1] = radius * Math.cos(angle);
}
// -- Init buffers
var vertexDataBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var positions = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0
]);
var vertexPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var texCoords = new Float32Array([
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,
1.0, 0.0,
0.0, 0.0,
0.0, 1.0
]);
var vertexTexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// -- Init Texture
// used for draw framebuffer storage
var FRAMEBUFFER_SIZE = {
x: canvas.width,
y: canvas.height
};
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.bindTexture(gl.TEXTURE_2D, null);
// -- Init Frame Buffers
var FRAMEBUFFER = {
RENDERBUFFER: 0,
COLORBUFFER: 1
};
var framebuffers = [
gl.createFramebuffer(),
gl.createFramebuffer()
];
var colorRenderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.COLORBUFFER]);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// -- Init VertexArray
var vertexArrays = [
gl.createVertexArray(),
gl.createVertexArray()
];
var vertexPosLocation = 0; // set with GLSL layout qualifier
gl.bindVertexArray(vertexArrays[PROGRAM.TEXTURE]);
gl.enableVertexAttribArray(vertexPosLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindVertexArray(null);
gl.bindVertexArray(vertexArrays[PROGRAM.SPLASH]);
gl.enableVertexAttribArray(vertexPosLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var vertexTexLocation = 1; // set with GLSL layout qualifier
gl.enableVertexAttribArray(vertexTexLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
gl.vertexAttribPointer(vertexTexLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindVertexArray(null);
// -- Render
// Pass 1
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.clearBufferfv(gl.COLOR, 0, [0.0, 1.0, 0.0, 0.0]);
gl.useProgram(programs[PROGRAM.TEXTURE]);
gl.bindVertexArray(vertexArrays[PROGRAM.TEXTURE]);
var IDENTITY = mat4.create();
gl.uniformMatrix4fv(mvpLocationTexture, false, IDENTITY);
gl.enable(gl.blend);
gl.blendFunc(gl.SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.LINE_LOOP, 0, vertexCount);
// Blit framebuffers, no Multisample texture 2d in WebGL 2
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, framebuffers[FRAMEBUFFER.COLORBUFFER]);
gl.blitFramebuffer(
0, 0, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y,
0, 0, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y,
gl.COLOR_BUFFER_BIT, gl.NEAREST
);
// Pass 2
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.useProgram(programs[PROGRAM.SPLASH]);
gl.uniform1i(diffuseLocation, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindVertexArray(vertexArrays[PROGRAM.SPLASH]);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var scaleVector3 = vec3.create();
vec3.set(scaleVector3, 8.0, 8.0, 8.0);
var mvp = mat4.create();
mat4.scale(mvp, IDENTITY, scaleVector3);
gl.uniformMatrix4fv(mvpLocation, false, mvp);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// -- Delete WebGL resources
gl.deleteBuffer(vertexPosBuffer);
gl.deleteBuffer(vertexTexBuffer);
gl.deleteTexture(texture);
gl.deleteRenderbuffer(colorRenderbuffer);
gl.deleteFramebuffer(framebuffers[FRAMEBUFFER.RENDERBUFFER]);
gl.deleteFramebuffer(framebuffers[FRAMEBUFFER.COLORBUFFER]);
gl.deleteVertexArray(vertexArrays[PROGRAM.TEXTURE]);
gl.deleteVertexArray(vertexArrays[PROGRAM.SPLASH]);
gl.deleteProgram(programs[PROGRAM.TEXTURE]);
gl.deleteProgram(programs[PROGRAM.SPLASH]);
})();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block }
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js" integrity="sha256-+09xst+d1zIS41eAvRDCXOf0MH993E4cS40hKBIJj8Q=" crossorigin="anonymous"></script>
<script>
(function () {
'use strict';
window.getShaderSource = function(id) {
return document.getElementById(id).textContent.replace(/^\s+|\s+$/g, '');
};
function createShader(gl, source, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
window.createProgram = function(gl, vertexShaderSource, fragmentShaderSource) {
var program = gl.createProgram();
var vshader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
var fshader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
gl.attachShader(program, vshader);
gl.deleteShader(vshader);
gl.attachShader(program, fshader);
gl.deleteShader(fshader);
gl.linkProgram(program);
var log = gl.getProgramInfoLog(program);
if (log) {
console.log(log);
}
log = gl.getShaderInfoLog(vshader);
if (log) {
console.log(log);
}
log = gl.getShaderInfoLog(fshader);
if (log) {
console.log(log);
}
return program;
};
})();
</script>
<!-- vertex shader -->
<!-- WebGL 2 shaders -->
<script id="vs-render" type="x-shader/x-vertex">
#version 300 es
#define POSITION_LOCATION 0
precision highp float;
precision highp int;
uniform mat4 MVP;
layout(location = POSITION_LOCATION) in vec2 position;
void main()
{
gl_Position = MVP * vec4(position, 0.0, 1.0);
}
</script>
<script id="fs-render" type="x-shader/x-fragment">
#version 300 es
precision highp float;
precision highp int;
out vec4 color;
void main()
{
color = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script id="vs-splash" type="x-shader/x-vertex">
#version 300 es
precision highp float;
precision highp int;
uniform mat4 MVP;
layout(location = 0) in vec2 position;
layout(location = 1) in vec2 texcoord;
out vec2 uv;
void main()
{
uv = texcoord;
gl_Position = MVP * vec4(position, 0.0, 1.0);
}
</script>
<script id="fs-splash" type="x-shader/x-fragment">
#version 300 es
precision highp float;
precision highp int;
uniform sampler2D diffuse;
in vec2 uv;
out vec4 color;
void main()
{
color = texture(diffuse, uv);
}
</script>
<script>
</script>
说问题似乎与'alpha=false creation parameter of the webgl2 context.'有关表明问题是canvas如何与网页本身混合?
A canvas 与页面中 HTML 的其余部分合成(混合),无论 canvas 后面是什么。 canvas 元素本身可以有一个 CSS 背景,页面 <body>
可以有一个背景。 canvas 可以覆盖其他元素。无论它与页面合成。
默认使用blendFunc(ONE, ONE_MINUS_SRC_ALPHA)
有效混合,因此canvas中的颜色需要预乘alpha值。
您可以将 canvas 设置为没有 alpha getContext("webgl", {alpha: false})
在这种情况下,
您还可以告诉浏览器您的像素值是未预乘的 alpha,在这种情况下,它将有效地使用 blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
来合成页面中的 canvas。您使用 getContext("webgl, {premultipliedAlpha: false}
.
我建议您将 canvas 的背景颜色设置为可以清楚显示正在发生的事情的颜色。例如。
canvas {
background-color: #FF0;
background-image:
linear-gradient(45deg, #F0F 25%, transparent 25%),
linear-gradient(-45deg, #F0F 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #F0F 75%),
linear-gradient(-45deg, transparent 75%, #F0F 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
<canvas></canvas>
然后画东西
function main(attribs = {}) {
const canvas = document.createElement('canvas');
canvas.width = 150;
canvas.height = 50;
document.body.appendChild(canvas);
const gl = canvas.getContext('webgl2', attribs);
if (!gl) {
return alert('need webgl2');
}
log('attribs:', JSON.stringify(gl.getContextAttributes()));
const vs = `
attribute vec4 position;
void main() {
gl_Position = position;
}
`;
const fs = `
precision highp float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: [
-0.8, 0.5, 0,
0.8, 0.4, 0,
0.0,-0.5, 0,
],
});
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {color: [0, 1, 0, 1]});
twgl.drawBufferInfo(gl, bufferInfo);
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
main({});
main({premultipliedAlpha: false});
main({antialias: false});
canvas {
image-rendering: pixelated;
background-color: #FF0;
background-image:
linear-gradient(45deg, #F0F 25%, transparent 25%),
linear-gradient(-45deg, #F0F 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #F0F 75%),
linear-gradient(-45deg, transparent 75%, #F0F 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
如果我把它们炸毁,你就会看到区别。您还可以看到它正在从 HTML.
的背景中混合注意:我没有设置 MSAA 渲染缓冲区,因为通常 canvas 默认情况下本身就是一个 MSAA 渲染缓冲区。这实际上取决于浏览器,但很明显我的 GPU 上的浏览器默认为 canvas.
使用 MSAA如果不清楚,我的建议是你的结果是你没有设置 {alpha: false}
所以如果你的 canvas 中有任何非 1.0 alpha 值那么您会看到与 canvas.
即使是白色背景,我们也会看到一些问题,具体取决于设置
另请注意,您提到清除到 (1, 0, 0, 0)。 1, 0, 0, 0 是默认 "premultipliedAlpha: true" canvas 上的无效颜色。如果预乘颜色,R 不能 > A。
在那种情况下会发生什么是不确定的。过去,firefox 和 chrome 对这些颜色的表现非常不同。