OpenGL之混合
首先说说透明度吧,平时我们怎么说明一个物体是透明,想必是将此物体和背后的物体混合起来看,发现只看到后面的物体,所以就说这个物体是透明的。
实际上物体透明分成两种透明,一种是完全透明另一种是部分透明,完全透明的话会使颜色完全穿透,而部分透明使颜色穿透的同时也显示自身颜色。描述一个物体的透明度我们引入颜色值中的Alpha值。当Alpha值为0.0时,那么该物体完全透明,1.0则是完全不透明。
接下来就是如何去载入一个有透明部分的纹理:
此时就要告诉SOIL库载入放是要选择GL_RGBA方式,同时设置纹理时也要改成GL_RGBA方式。
unsigned char * image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGBA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
片段着色器中
void main()
{
// color = vec4(vec3(texture(texture1, TexCoords)), 1.0);
color = texture(texture1, TexCoords);
}
如果仅仅只做以上修改,那么显示出来的结果这样:,原因是OpenGL默认是不知道如何处理alpha值的,此时就要用到GLSL语言中的discard,有点类似于编程语言中break味道,它保证了片段不会被进一步处理,这样就不会进入颜色缓冲。
所以着色器要这样设置
但是上面这样做的方式是不能渲染出部分透明的纹理,他们要么是完全透明然后被discard,要么只渲染不透明部分。所以为了渲染出不同的透明度级别,我们需要开启混合(Blending)
OpenGL混合的方式:
例如我把绿色物体渲染到红色物体上,那么此时目标颜色是红色,源颜色是绿色。至于因子,先设置源因子就是源向量的Alpha值,然后目标因子是1.0-源因子
那么该如何设置这样的混合呢?
glBlendFunc
的函数。
void glBlendFunc(GLenum sfactor, GLenum dfactor)
接收两个参数用来设置源(source)和目标(destination)因子。其中参数选项如下:
此例子中我们这样设置:glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
之后开始应用混合:
首先要开启混合glEnable(GL_BLEND)
设置混合处理方式glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
由于我们开启了混合,就不需要丢弃片段了所以片段着色器只需
但此时还会出现前面的窗子透明部分阻塞了后面的现象
此时原因是当混用深度测试和混合时,深度测试不关心片段是否有透明度,所以透明部分被写入深度缓冲,就和其他值没什么区别,那么后面的深度值比较大那么该片段就会被丢弃,此时就起不到透明的效果。
为保证前面窗子显示了它后面的窗子,我们必须首先绘制后面的窗子。这意味着我们必须手工调整窗子的顺序,从远到近地逐个渲染,这仅仅是对于要透明部分的物体,如果对于不要透明部分的物体,无需混合仅仅丢弃即可。
所以我们遵循下面原则来修改:
当无透明度物体和透明物体一起绘制的时候,通常要先绘制所有不透明物体,为所有透明物体排序,按顺序绘制透明物体。 一种排序透明物体的方式是,获取一个物体到观察者透视图的距离。这可以通过获取摄像机的位置向量和物体的位置向量来得到。接着我们就可以把它和相应的位置向量一起储存到一个map中。map会自动基于它的键排序它的值。
计算z值并排序
std::map<float, glm::vec3> sorted;
for (GLuint i = 0; i < windows.size(); i++) // windows contains all window positions
{
GLfloat distance = glm::length(camera.Position - windows[i]);
sorted[distance] = windows[i];
}
之后绘制只需要逆序绘制即可
最终结果