OpenGL 异常混合结果
OpenGL Abnormal blending result
网格(vertexBuffer 和 indexBuffer):
几个二维三角形相互重叠。每个顶点的顶点颜色为R:1, G:0, B:0, A:0.005
(透明红色)。 三角形合并为一个网格。
渲染代码:(C#代码,其他语言应该是一样的)
GL.Enable(GL.GL_BLEND);
GL.BlendEquation(GL.GL_FUNC_ADD_EXT);
GL.BlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
GL.Disable(GL.GL_CULL_FACE);
GL.Disable(GL.GL_DEPTH_TEST);
GL.Disable(GL.GL_ALPHA_TEST);
GL.DepthFunc(GL.GL_NEVER);
GL.Enable(GL.GL_SCISSOR_TEST);
GL.ClearColor(1, 1, 1, 1);
GL.Clear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
GL.Viewport(0, 0, width, height);
GLM.mat4 ortho_projection = GLM.glm.ortho(0.0f, width, height, 0.0f, -5.0f, 5.0f);
material.program.Bind();
material.program.SetUniformMatrix4("ProjMtx", ortho_projection.to_array());
GL.BindVertexArray(material.vaoHandle);
GL.BindBuffer(GL.GL_ARRAY_BUFFER, material.VboHandle);
GL.BufferData(GL.GL_ARRAY_BUFFER, vertexBuffer.Count*sizeof_Vertex, vertexBuffer.Pointer, GL.GL_STREAM_DRAW);
GL.BindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, material.elementsHandle);
GL.BufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.Count *sizeof_Index, indexBuffer.Pointer, GL.GL_STREAM_DRAW);
GL.DrawElements(GL.GL_TRIANGLES, elemCount, GL.GL_UNSIGNED_INT, IntPtr.Zero);
顶点着色器
#version 330
uniform mat4 ProjMtx;
in vec2 Position;
in vec2 UV;
in vec4 Color;
out vec2 Frag_UV;
out vec4 Frag_Color;
void main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy,0,1);
}
片段着色器
#version 330
in vec2 Frag_UV;
in vec4 Frag_Color;
out vec4 Out_Color;
void main()
{
Out_Color = Frag_Color;
}
我以为重叠的三角形会这样渲染:
但实际渲染结果是:
每次绘制和SwapBuffer
调用后,渲染出来的多边形的红色值越来越强
为什么?那些透明的三角形不应该只混合一次吗?我的意思是,随着时间的推移,渲染结果应该是静态的,而不是动画的。
请注意,我希望重叠的像素以更深的红色呈现。但我不希望 整个 网格(重叠的三角形)渲染成更红的颜色 逐帧 。由于当前绑定的帧缓冲区已被清除,因此混合工作正常,渲染结果应该不会越来越红。
您已禁用 GL_DEPTH_TEST
,您没有模板测试,也没有指定其他操作来阻止绘制片段。
混合函数是目标颜色和源颜色的函数。每次片段都绘制颜色
目标缓冲区更改。
如果您设置 glBlendFunc
使用参数 (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
则目标颜色为
计算如下:
C_dest = C_src * A_src + C_dest * (1-A_src)
如果将 C_dest = 0
与 C_src = 1.0
和 A_src = 0.05
混合,则:
C_dest = 0.05 = 1.0 * 0.05 + 0.0 * 0.95
如果您重复混合相同的颜色 C_src = 1.0
和 A_src = 0.05
,则目标颜色会变亮:
C_dest = 0.0975 = 1.0 * 0.05 + 0.05 * 0.95
重复此操作会使目标颜色越来越亮。
要防止这种情况,您可以使用深度测试。
例如,使用深度函数 GL_LESS
,如果片段的深度等于或什至高于存储在深度缓冲区中的深度,则阻止绘制片段:
GL.Enable(GL.GL_DEPTH_TEST);
GL.DepthFunc(GL.GL_LESS);
或者您可以设置模板缓冲区和模板测试。
例如,可以将模板测试设置为仅在模板缓冲区等于 0 时通过。
每次要写入片段时,模板缓冲区都会递增。如果同一个片段要写第二次,则测试失败:
GL.Clear( GL.GL_STENCIL_BUFFER_BIT );
GL.Enable( GL.GL_STENCIL_TEST );
GL.StencilOp( GL.GL_KEEP, GL.GL_KEEP, GL.GL_INCR );
GL.StencilFunc( GL.GL_EQUAL, 0, 255 );
答案的扩展
注意,GL.Clear(GL.GL_COLOR_BUFFER_BIT);
确实会立即清除绘图缓冲区的颜色平面,当然您必须在每次刷新场景之前执行此操作,并且它只会影响当前绑定的帧缓冲区。
此外,必须正确设置帧缓冲区写入操作的颜色掩码。
GL.ColorMask(GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE);
如果您使用双缓冲区,请确保缓冲区每次刷新仅交换一次,而不是两次。
请注意,您必须注意几何体没有重叠并且只绘制一次!
网格(vertexBuffer 和 indexBuffer):
几个二维三角形相互重叠。每个顶点的顶点颜色为R:1, G:0, B:0, A:0.005
(透明红色)。 三角形合并为一个网格。
渲染代码:(C#代码,其他语言应该是一样的)
GL.Enable(GL.GL_BLEND);
GL.BlendEquation(GL.GL_FUNC_ADD_EXT);
GL.BlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
GL.Disable(GL.GL_CULL_FACE);
GL.Disable(GL.GL_DEPTH_TEST);
GL.Disable(GL.GL_ALPHA_TEST);
GL.DepthFunc(GL.GL_NEVER);
GL.Enable(GL.GL_SCISSOR_TEST);
GL.ClearColor(1, 1, 1, 1);
GL.Clear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
GL.Viewport(0, 0, width, height);
GLM.mat4 ortho_projection = GLM.glm.ortho(0.0f, width, height, 0.0f, -5.0f, 5.0f);
material.program.Bind();
material.program.SetUniformMatrix4("ProjMtx", ortho_projection.to_array());
GL.BindVertexArray(material.vaoHandle);
GL.BindBuffer(GL.GL_ARRAY_BUFFER, material.VboHandle);
GL.BufferData(GL.GL_ARRAY_BUFFER, vertexBuffer.Count*sizeof_Vertex, vertexBuffer.Pointer, GL.GL_STREAM_DRAW);
GL.BindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, material.elementsHandle);
GL.BufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.Count *sizeof_Index, indexBuffer.Pointer, GL.GL_STREAM_DRAW);
GL.DrawElements(GL.GL_TRIANGLES, elemCount, GL.GL_UNSIGNED_INT, IntPtr.Zero);
顶点着色器
#version 330
uniform mat4 ProjMtx;
in vec2 Position;
in vec2 UV;
in vec4 Color;
out vec2 Frag_UV;
out vec4 Frag_Color;
void main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy,0,1);
}
片段着色器
#version 330
in vec2 Frag_UV;
in vec4 Frag_Color;
out vec4 Out_Color;
void main()
{
Out_Color = Frag_Color;
}
我以为重叠的三角形会这样渲染:
但实际渲染结果是:
每次绘制和SwapBuffer
调用后,渲染出来的多边形的红色值越来越强
为什么?那些透明的三角形不应该只混合一次吗?我的意思是,随着时间的推移,渲染结果应该是静态的,而不是动画的。
请注意,我希望重叠的像素以更深的红色呈现。但我不希望 整个 网格(重叠的三角形)渲染成更红的颜色 逐帧 。由于当前绑定的帧缓冲区已被清除,因此混合工作正常,渲染结果应该不会越来越红。
您已禁用 GL_DEPTH_TEST
,您没有模板测试,也没有指定其他操作来阻止绘制片段。
混合函数是目标颜色和源颜色的函数。每次片段都绘制颜色 目标缓冲区更改。
如果您设置 glBlendFunc
使用参数 (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
则目标颜色为
计算如下:
C_dest = C_src * A_src + C_dest * (1-A_src)
如果将 C_dest = 0
与 C_src = 1.0
和 A_src = 0.05
混合,则:
C_dest = 0.05 = 1.0 * 0.05 + 0.0 * 0.95
如果您重复混合相同的颜色 C_src = 1.0
和 A_src = 0.05
,则目标颜色会变亮:
C_dest = 0.0975 = 1.0 * 0.05 + 0.05 * 0.95
重复此操作会使目标颜色越来越亮。
要防止这种情况,您可以使用深度测试。
例如,使用深度函数 GL_LESS
,如果片段的深度等于或什至高于存储在深度缓冲区中的深度,则阻止绘制片段:
GL.Enable(GL.GL_DEPTH_TEST);
GL.DepthFunc(GL.GL_LESS);
或者您可以设置模板缓冲区和模板测试。 例如,可以将模板测试设置为仅在模板缓冲区等于 0 时通过。 每次要写入片段时,模板缓冲区都会递增。如果同一个片段要写第二次,则测试失败:
GL.Clear( GL.GL_STENCIL_BUFFER_BIT );
GL.Enable( GL.GL_STENCIL_TEST );
GL.StencilOp( GL.GL_KEEP, GL.GL_KEEP, GL.GL_INCR );
GL.StencilFunc( GL.GL_EQUAL, 0, 255 );
答案的扩展
注意,GL.Clear(GL.GL_COLOR_BUFFER_BIT);
确实会立即清除绘图缓冲区的颜色平面,当然您必须在每次刷新场景之前执行此操作,并且它只会影响当前绑定的帧缓冲区。
此外,必须正确设置帧缓冲区写入操作的颜色掩码。
GL.ColorMask(GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE);
如果您使用双缓冲区,请确保缓冲区每次刷新仅交换一次,而不是两次。
请注意,您必须注意几何体没有重叠并且只绘制一次!