不使用混合如何实现透明度
通过在片源着色器中添加if (texColor.a < 0.1) discard;
来根据透明度的值判断是否丢弃片段,丢弃的片段不会被进一步处理,不会进入颜色缓冲。效果如下。
这里有个问题是草的顶部可以看到一个有色边框,这是因为当采样纹理边缘的时候,OpenGL会对边缘的值和纹理下一个重复的值进行插值(因为我们将它的环绕方式设置为了GL_REPEAT。)
为了避免这个问题当我们使用alpha纹理的时候,要将纹理的环绕方式设置为GL_CLAMP_TO_EGDGE
。
使用混合
虽然可以直接丢弃片段,但我们还是不能渲染半透明的图像,只能要么渲染要么丢弃。要想渲染多个透明度级别的图像,需要启用混合。
启用混合
glEnable(GL_BLEND);
混合方程
C_result = C_source ∗ F_source + C_destination ∗ F_destination
- C_source:源颜色向量。这是源自纹理的颜色向量。
- C_destination:目标颜色向量。这是当前储存在颜色缓冲中的颜色向- 量。
- F_source:源因子值。指定了alpha值对源颜色的影响。
- F_destination:目标因子值。指定了alpha值对目标颜色的影响。
片段着色器运行完成后,并且所有的测试都通过之后,这个混合方程才会应用到片段颜色输出与当前颜色缓冲中的值(当前片段之前储存的之前片段的颜色)上。源颜色和目标颜色将会由OpenGL自动设定,但源因子和目标因子的值可以由我们来决定。
设置源因子和目标因子
glBlendFunc(GLenum sfactor, GLenum dfactor)
glBlendFuncSeparate 可以为RGB和alpha通道分别设置不同的值
glBlendColor 可以设置常数颜色向量C_constant
可选值有
-
GL_ZERO
因子等于0 -
GL_ONE
因子等于1 -
GL_SRC_COLOR
因子等于源颜色向量C_source -
GL_ONE_MINUS_SRC_COLOR
因子等于1−C_source -
GL_DST_COLOR
因子等于目标颜色向量C_destination -
GL_ONE_MINUS_DST_COLOR
因子等于1−C_destination -
GL_SRC_ALPHA
因子等于C_source的alpha分量 -
GL_ONE_MINUS_SRC_ALPHA
因子等于1− C_source的alpha分量 -
GL_DST_ALPHA
因子等于C¯destination的alpha分量 -
GL_ONE_MINUS_DST_ALPHA
因子等于1− C_destination的alpha分量 -
GL_CONSTANT_COLOR
因子等于常数颜色向量C_constant -
GL_ONE_MINUS_CONSTANT_COLOR
因子等于1−C_constant -
GL_CONSTANT_ALPHA
因子等于C_constant的alpha分量 -
GL_ONE_MINUS_CONSTANT_ALPHA
因子等于1− C_constant的alpha分量
改变方程中源和目标部分的运算符
glBlendEquation(GLenum mode)允许我们设置运算符
-
GL_FUNC_ADD
:默认选项,将两个分量相加:C=Src+Dst。 -
GL_FUNC_SUBTRACT
:将两个分量相减: C=Src−Dst。 -
GL_FUNC_REVERSE_SUBTRACT
:将两个分量相减,但顺序相反:C=Dst−Src。
开始使用混合
image.pngglEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
可以发现最前面窗户的透明部分遮蔽了背后的窗户,因为写入深度缓冲时,深度缓冲不会检查片段是否是透明的,所以透明的部分会和其它值一样写入到深度缓冲中。结果就是窗户的整个四边形不论透明度都会进行深度测试。即使透明的部分应该显示背后的窗户,深度测试仍然丢弃了它们。
要想保证窗户中能够显示它们背后的窗户,我们需要首先绘制背后的这部分窗户。也就是我们必须先手动将窗户按照最远到最近来排序,再按照顺序渲染。
绘制顺序规则需要如下
- 先绘制所有不透明的物体。
- 对所有透明的物体排序。
- 按顺序绘制所有透明的物体。
排序透明物体的一种方法是,从观察者视角获取物体的距离。这可以通过计算摄像机位置向量和物体的位置向量之间的距离所获得。
添加代码根据举例排序然后按顺序绘制
std::map<float, glm::vec3> sorted;
for (unsigned int i = 0; i < vegetation.size(); i++) {
float distance = glm::length(camera.Position - vegetation[i]);
sorted[distance] = vegetation[i];
}
image.png
按照距离排序物体这种方法并没有考虑旋转、缩放或者其它的变换,奇怪形状的物体需要一个不同的计量,而不是仅仅一个位置向量。
在场景中排序物体是一个很困难的技术,更高级的技术有次序无关透明度(Order Independent Transparency, OIT)。
网友评论