我正在尝试渲染两个对象,使用两个单独的 gl.drawArrays
调用。我想让对象的透明部分不可见。此外,我想在另一个对象之上渲染一个对象,这样第一个绘制的对象在与第二个对象重叠的地方不可见。
我在我的渲染循环中使用这个设置:
gl.clearColor(0, 0, 0, 1);
// https://stackoverflow.com/questions/18439897/webgl-fragment-shader-opacity
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
gl.enable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
我不确定混合函数的作用,但我使用它们来启用透明度。但这会导致两个物体混合并产生黄色。 (一个物体是红色的,另一个是绿色的)。如果我最后画红色,我想要红色,反之亦然,同时启用透明度。
const fShaderSource2 = `#version 300 es
precision mediump float;
out vec4 outColor;
void main() {
outColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`;
const fShaderSource = `#version 300 es
precision mediump float;
out vec4 outColor;
uniform sampler2D u_texture;
void main() {
outColor = texture(u_texture, vec2(0.0));
}
`;
const vShaderSource = `#version 300 es
precision mediump float;
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
`;
main(document.getElementById('app'));
function main(element) {
const canvas = document.createElement('canvas'),
gl = canvas.getContext('webgl2');
element.append(canvas);
const displayWidth = canvas.clientWidth,
displayHeight = canvas.clientHeight;
canvas.width = displayWidth;
canvas.height = displayHeight;
let graphics = new Graphics({width: displayWidth, height: displayHeight}, gl);
new Loop(() => {
graphics.render();
}).start();
}
function Graphics(state, gl) {
const { width, height } = state;
gl.clearColor(0, 0, 0, 0);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
gl.enable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
let minibatch = [];
const redText = makeGlQuad(gl, fShaderSource, canvasTexture());
const greenText = makeGlQuad(gl, fShaderSource2);
this.render = () => {
minibatch.push(redText);
minibatch.push(greenText);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
minibatch.forEach(({
program,
resUniformLocation,
vao,
glTexture
}) => {
gl.useProgram(program);
gl.uniform2f(resUniformLocation, gl.canvas.width, gl.canvas.height);
if (glTexture) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, glTexture);
}
gl.bindVertexArray(vao);
gl.drawArrays(gl.TRIANGLES, 0, 6);
});
minibatch = [];
};
}
function makeGlQuad(gl, fShaderSource, texture) {
let vShader = createShader(gl, gl.VERTEX_SHADER, vShaderSource);
let fShader = createShader(gl, gl.FRAGMENT_SHADER, fShaderSource);
let program = createProgram(gl, vShader, fShader);
let posAttrLocation = gl.getAttribLocation(program, "a_position");
let posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
/*
(-1, 1).( 1, 1)
.
(-1,-1).( 1,-1)
*/
let positions = [
-1, 1,
-1, -1,
1, -1,
-1, 1,
1,-1,
1, 1
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(posAttrLocation);
let size = 2,
type = gl.FLOAT,
normalize = false,
stride = 0,
offset = 0;
gl.vertexAttribPointer(posAttrLocation,
size,
type,
normalize,
stride,
offset);
let glTexture;
if (texture) {
glTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture);
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));
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_MIN_FILTER, gl.LINEAR);
}
let resUniformLocation = gl.getUniformLocation(program, "u_resolution");
let texUniformLocation = gl.getUniformLocation(program, "u_texture");
return {
program,
resUniformLocation,
vao,
glTexture
}
}
function canvasTexture() {
return withCanvasTexture(256, 256, (w, h, canvas, ctx) => {
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, w, h);
ctx.font = '50pt Comic Sans';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('label', w / 2, 50);
return canvas;
});
function withCanvasTexture(width, height, f) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
f(width, height, canvas, canvas.getContext('2d'));
const texture = canvas;
//document.body.append(canvas);
return texture;
}
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
};
function createProgram(gl, vShader, fShader) {
let program = gl.createProgram();
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.error(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
// Loop Library
function Loop(fn) {
const perf = window.performance !== undefined ? window.performance : Date;
const now = () => perf.now();
const raf = window.requestAnimationFrame;
let running = false,
lastUpdate = now(),
frame = 0;
this.start = () => {
if (running) {
return this;
}
running = true;
lastUpdate = now();
frame = raf(tick);
return this;
};
this.stop = () => {
running = false;
if (frame != 0) {
raf.cancel(frame);
}
frame = 0;
return this;
};
const tick = () => {
frame = raf(tick);
const time = now();
const dt = time - lastUpdate;
fn(dt);
lastUpdate = time;
};
}
#app canvas {
position: fixed;
top: 50%;
bottom: 0;
left: 50%;
right: 0;
width: 100vmin;
height: 70vmin;
transform: translate(-50%, -25%);
image-rendering: optimizeSpeed;
cursor: none;
margin: auto;
}
<div id="app">
</div>
你可以在这里看到:
minibatch.push(redText);
minibatch.push(greenText);
我先渲染红色,然后渲染绿色,但我变成了黄色。
最佳答案
问题是您使用了错误的混合函数 (blendFunc
)。混合定义了一个函数,它将片段颜色输出与颜色缓冲区中的当前颜色相结合。第一个参数是片段颜色输出的因子,第二个参数是颜色缓冲区中颜色的因子。颜色相加,因为默认混合方程 (blendEquation()
) 是 FUNC_ADD
。
混合函数
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
可以用公式表示
destColor = srcColor * srcAlpha + destColor * 1
其中 destColor
是帧缓冲区中的当前颜色,srcColor
是设置为片段的颜色 (outColor
)。
这会导致当前帧缓冲区中的颜色被保留(乘以 1)。新颜色乘以 alpha channel 并添加到帧缓冲区中的颜色。如果帧缓冲区中的颜色为红色 (1, 0, 0),而新颜色为绿色 (0, 1, 0),则结果为黄色(如果 alpha channel 为 1):
(0, 1, 0) * 1 + (1, 0, 0) * 1 == (1, 1, 0)
使用混合函数:
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
这会导致 frambuffer 中的颜色和新颜色“混合”,这取决于新颜色的 alpha channel :
destColor = srcColor * srcAlpha + destColor * (1-srcAlpha)
Alpha blending的总则与 OpenGL 中的类似(因为 WebGL (2.0) 与 OpenGL ES (3.0) 非常一致),因此可以在 OpenGL 维基页面 Blending 上找到更多信息。 .
关于javascript - 如何渲染对象而不混合 webgl 中启用的透明度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57612782/