所有Blend混合在索引颜色模式下是非法的。混合所在的阶段是图形drawcall最后帧写入时候和之前drawcall写入的像素进行blend,如果前面检查如
sissortest, alphatest,, stenciltest,Depthtest不过那么不会进行混合, 为了混合有时候需要关闭depthtest实现想要的效果。
1.混合公式
假设:
源混合因子(Sr, Sg, Sb, Sa), 源颜色(Rs, Gs, Bs, As)
目标混合因子(Dr, Dg, Db, Da), 目标颜色(Rd, Gd, Bd, Ad)
则标准混合输出颜色 = (Sr*Rs + Dr*Rd, Sg*Gs + Dg*Gd , Sb*Bs + Db*Bd, Sa*As + Da*Ad)。
混合公式的指定:
通过glBlendEquation(GLenum mode);
glBlendEquationSeparate(GLenum modeRGB, GLenum modeApha);
#define GL_FUNC_ADD 0x8006 // Cs*S + Cd*D, 默认也是采用这种方式
#define GL_FUNC_SUBTRACT 0x800A // Cs*S - Cd*D
#define GL_FUNC_REVERSE_SUBTRACT 0x800B // -Cs*S + Cd*D
#define GL_MIN 0x8007 // min(Cs*S, Cd*D),每个分量都进行这样的操作
#define GL_MAX 0x8008 // max(Cs*S, Cd*D),每个分量都进行这样的操作
#define GL_LOGIC_OP 0x0BF1 // Cs op Cd; GL_LOGIC_OP通过glLogicOp()指定具体的颜色逻辑操作符。
2.混合因子指定
GL_ZERO: 表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
源混合因子和目标混合因子都是用一样的形式表示的(例如GL_SRC_ALPHA可用于源也可用于目标混合因子),如果用了GL_CONSTANT_ALPHA等CONSTANT类型的作为混合因子,那么glBlendColor需要指定混合因子的颜色通道值。
混合因子的指定:
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLAPI void GLAPIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor);指定源和目标混合因子。
glBlendFuncSeparate(); 将颜色和alpha混合因子分开来指定。
typedef void (GLAPIENTRY * PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
3.启用和关闭混合
glEnable(GL_BLEND);
glDisable(GL_BLEND);
使用GL_ZERO, GL_ONE也可以禁用混合,这个是默认设置。
OGL 2.0后引入了多个缓存区,OGL 3.0之前所有缓存区混合都是同时打开关闭,OGL3.0后可以为单个缓存区设置开启和关闭混合。
4.混合设置和实例技术
1)billboard技术实现
可以用GL_CONSTANT_ALPHA来设置指定的alpha值。billboard技术就是使用一个矩形平面上面设置混合的透明度实现想要的物体效果,当观察者移动时候billboard根据和摄像机的位置(每帧绘制时候都进行视图变换)进行旋转,实现始终面对着摄像机。
混合也可以实现抗锯齿,使得边缘的alpha值低。
2)从前向后,从后向前的渲染物体进行混合
draw call的顺序对绘制影响很大
,包括混合,所以unity中有渲染顺序是从前向后,还是从后向前的渲染顺序。
不启用深度测试下,当新进的z值小的像素不管是透不透明的,目标像素中是透明的还是不透明的,会不会剔除看混合操作公式,和混合因子的设置。
启用深度测试下,不管透不透明,都是基于深度z值的,新进的只有小于(大于等于都无法写入)才能写入。
像素写入测试中,一个测试失败将不写入,也不再进行后面的测试。
OpenGL测试的顺序是:剪裁测试、Alpha测试、模板测试、深度测试。如果某项测试不通过,则不会进行下一步,而只有所有测试都通过的情况下才会执行混合操作。
要实现3D效果中的混合操作,那么写入不透明物体时候启用深度测试可读可写,写入具有透明度的物体时候,启用深度测试只读不写,因此可以实现通过具有透明度的物体观察其它物体的现象
。在Drawcall前可以用glDepthMask(GL_FALSE)设置为只读,glDepthMask(GL_TRUE)可写可读。
3)实例
#define GLEW_STATIC
#include <Gl/glew.h>
#include <GL/glut.h>
#include <stdlib.h>
#pragma comment(lib, "glew32s.lib")
void init(void)
{
glClearColor(1.0, 1.0, 0.0, 0.5);
glewInit();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(1.0, 0.0, 0.0, 0.5);
glRectf(-0.5,-0.5,0.5,0.5);
// draw call的顺序,谁绘制在前面就是目标像素,绘制后面是源像素 来进行混合
/* if (leftFirst) {
drawLeftTriangle();
drawRightTriangle();
}
else {
drawRightTriangle();
drawLeftTriangle();
}*/
glFlush();
}
void reshape(int w, int h)
{
GLdouble aspect = (GLdouble) w / h;
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (aspect < 1.0) {
aspect = 1.0 / aspect;
glOrtho(-aspect, aspect, -1.0, 1.0, -1.0, 1.0);
} else
glOrtho(-1.0, 1.0, -aspect, aspect, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'a': case 'A':
/* Colors are added as: (1, 1, 0) + (0, 0, 1) = (1, 1, 1)
* which will produce a white square on a yellow background.
*/
glBlendEquation(GL_FUNC_ADD);
break;
case 's': case 'S':
/* Colors are subtracted as: (0, 0, 1) - (1, 1, 0) = (-1, -1, 1)
* which is clamped to (0, 0, 1), producing a blue square on a
* yellow background
*/ glBlendEquation(GL_FUNC_SUBTRACT);
break;
case 'r': case 'R':
/* Colors are subtracted as: (1, 1, 0) - (0, 0, 1) = (1, 1, -1)
* which is clamed to (1, 1, 0). This produces yellow for both
* the square and the background.
*/
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
break;
case 'm': case 'M':
/* The minimum of each component is computed, as
* [min(1, 0), min(1, 0), min(0, 1)] which equates to (0, 0, 0).
* This will produce a black square on the yellow background.
*/
glBlendEquation(GL_MIN);
break;
case 'x': case 'X':
/* The minimum of each component is computed, as
* [max(1, 0), max(1, 0), max(0, 1)] which equates to (1, 1, 1)
* This will produce a white square on the yellow background.
*/
glBlendEquation(GL_MAX);
break;
case 27:
exit(0);
}
glutPostRedisplay();
}
/* Main Loop
* Open window with initial window size, title bar,
* RGBA display mode, and handle input events.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(512,512);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
init();
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}