简体   繁体   English

如何在不与webgl中启用的透明度混合的情况下呈现对象

[英]How to render objects without blending with transparency enabled in webgl

I am trying to render two objects, with two separate gl.drawArrays call. 我试图用两个单独的gl.drawArrays调用渲染两个对象。 I want to have transparent parts of the objects not be visible. 我希望对象的透明部分不可见。 Also I want to render one object on top of another so the first drawn object is not visible where it overlaps the second one. 我也想在另一个对象上渲染一个对象,因此第一个绘制的对象在与第二个对象重叠的位置不可见。

I use this setup on my render loop: 我在渲染循环上使用此设置:

 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);

I am not sure what the blend functions do but I use them so the transparency is enabled. 我不确定混合功能是做什么的,但是我使用它们是为了启用透明度。 But that causes two objects to blend and create a yellow colour. 但这会导致两个对象混合并产生黄色。 (One object is red, other is green). (一个对象是红色,另一个是绿色)。 I want to have red if i draw red last, and vice versa, while having transparency enabled. 如果要最后启用红色,我想使用红色,反之亦然,同时启用透明度。

 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> 

You can see here: 您可以在这里看到:

minibatch.push(redText);
minibatch.push(greenText);

I render red first and then green second but I get yellow instead. 我先渲染红色,然后渲染绿色,但我却渲染黄色。

The issue is that you use the wrong blend function ( blendFunc ). 问题是您使用了错误的混合函数( blendFunc )。 Blending defines a function which combines the fragment color outputs with the current colors in the color buffers. 混合定义了将片段颜色输出与颜色缓冲区中当前颜色组合在一起的功能。 The fist parameter is the factor for the fragment color output and the 2nd parameter the factor for the color in the color buffer. 第一个参数是片段颜色输出的因数,第二个参数是颜色缓冲区中颜色的因数。 The colors are summed, because the default blend equation ( blendEquation() ) is FUNC_ADD . 对颜色求和,因为默认的混合方程式( blendEquation() )为FUNC_ADD

So the blend function 所以混合功能

 gl.blendFunc(gl.SRC_ALPHA, gl.ONE); 

can be expressed by the formula 可以用公式表示

destColor = srcColor * srcAlpha + destColor * 1 

where destColor is the current color in the framebuffer, srcColor is the color which is set to fragment ( outColor ). 其中destColor是帧缓冲区中的当前颜色, srcColor是设置为fragment( outColor )的颜色。
This causes that the color in the current framebuffer is kept (multiplied by 1). 这导致当前帧缓冲区中的颜色得以保留(乘以1)。 The new color is multiplied by the alpha channel and add to the color in the framebuffer. 新颜色乘以Alpha通道,然后添加到帧缓冲区中的颜色。 If the color in the framebuffer is red (1, 0, 0) and the new color is green (0, 1, 0), then the result is yellow (if the alpha channel is 1): 如果帧缓冲区中的颜色为红色(1、0、0),而新颜色为绿色(0、1、0),则结果为黄色(如果Alpha通道为1):

(0, 1, 0) * 1 + (1, 0, 0) * 1 == (1, 1, 0)    

Use the blend function : 使用blend函数:

gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

which cause that the color in the frambuffer and the new color are "mixed", dependent on the alpha channel of the new color: 这会导致frambuffer中的颜色和新颜色“混合”,具体取决于新颜色的alpha通道:

destColor = srcColor * srcAlpha + destColor * (1-srcAlpha)

The general principle of Alpha blending is similar as in OpenGL (since WebGL (2.0) conforms closely to the OpenGL ES (3.0)), so further information can be found at the OpenGL wiki page Blending . Alpha混合的一般原理与OpenGL中相似(因为WebGL(2.0)与OpenGL ES(3.0)非常一致),因此可以在OpenGL Wiki页面Blending上找到更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM