LearnOpenGL学习笔记——混合

混合(个人感觉叫透明度更好些)

OpenGL中,混合(Blending)通常是实现物体透明度(Transparency)的一种技术。明就是说一个物体(或者其中的一部分)不是纯色(Solid Color)的,它的颜色是物体本身的颜色和它背后其它物体的颜色的不同强度结合。一个有色玻璃窗是一个透明的物体,玻璃有它自己的颜色,但它最终的颜色还包含了玻璃之后所有物体的颜色。这也是混合这一名字的出处,我们混合(Blend)(不同物体的)多种颜色为一种颜色。所以透明度能让我们看穿物体。也就是我们通常说的RGBA,A指的alpha通道。
在这里插入图片描述

丢弃片段

下面是草的纹理,我们将草贴到一个四边形上,放到场景中。
在这里插入图片描述
所以当添加像草这样的植被到场景中时,我们不希望看到草的方形图像,而是只显示草的部分,并能看透图像其余的部分。我们想要丢弃(Discard)显示纹理中透明部分的片段,不将这些片段存储到颜色缓冲中。在此之前,我们还要学习如何加载一个透明的纹理:
加载图片的RGBA

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

从纹理中获取RGBA

void main()
{
    // FragColor = vec4(vec3(texture(texture1, TexCoords)), 1.0);
    FragColor = texture(texture1, TexCoords);
    // 如果纹理的alpha小于1,说明是透明纹理 ,则舍弃该片段
    if(texColor.a < 0.1)
        discard;
    FragColor = texColor;
}

渲染半透明纹理

虽然直接丢弃片段很好,但它不能让我们渲染半透明的图像。我们要么渲染一个片段,要么完全丢弃它。要想渲染有多个透明度级别的图像,我们需要启用混合(Blending)。和OpenGL大多数的功能一样,我们可以启用GL_BLEND来启用混合:

glEnable(GL_BLEND);

启用了混合之后,我们需要告诉OpenGL它该如何混合。OpenGL中的混合是通过下面这个方程来实现的:

在这里插入图片描述
使用glBlendFunc(GLenum sfactor, GLenum dfactor)函数接受两个参数,来设置源和目标因子。OpenGL为我们定义了很多个选项,我们将在下面列出大部分最常用的选项。注意常数颜色向量C¯constant可以通过glBlendColor函数来另外设置。

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分量

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);

OpenGL甚至给了我们更多的灵活性,允许我们改变方程中源和目标部分的运算符。当前源和目标是相加的,但如果愿意的话,我们也可以让它们相减。glBlendEquation(GLenum mode)允许我们设置运算符,它提供了三个选项:

GL_FUNC_ADD:默认选项,将两个分量相加:C¯result=Src+Dst。
GL_FUNC_SUBTRACT:将两个分量相减: C¯result=Src−Dst。
GL_FUNC_REVERSE_SUBTRACT:将两个分量相减,但顺序相反:C¯result=Dst−Src。

通常我们都可以省略调用glBlendEquation,因为GL_FUNC_ADD对大部分的操作来说都是我们希望的混合方程。

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

在这里插入图片描述
结果不正确,是因为混合和深度测试同时开启。深度测试将后面的片元直接丢弃。当写入深度缓冲时,深度缓冲不会检查片段是否是透明的,所以透明的部分会和其它值一样写入到深度缓冲中。结果就是窗户的整个四边形不论透明度都会进行深度测试。即使透明的部分应该显示背后的窗户,深度测试仍然丢弃了它们。
要想让混合在多个物体上工作,我们需要最先绘制最远的物理,最后绘制最近的物体。普通不需要混合的物体任然可以使用深度缓冲正常绘制。我们仍要保证它们在绘制(排序的)透明物体之前已经绘制完毕了。当绘制一个有不透明和透明物体的场景的时候,大体的原则如下:
1.先绘制所有不透明的物体
2.对所有透明物体排序
3.按顺序绘制所有透明物体
排序透明物体的一种方法是,从观察者视角获取物体的距离。这可以通过计算摄像机位置向量和物体的位置向量之间的距离所获得。接下来我们把距离和它对应的位置向量存储到一个STL库的map数据结构中。map会自动根据键值(Key)对它的值排序,所以只要我们添加了所有的位置,并以它的距离作为键,它们就会自动根据距离值排序了。

//通过计算摄像机位置向量和物体的位置向量之间的距离所获得
std::map<float, glm::vec3> sorted;
for (unsigned int i = 0; i < windows.size(); i++)
{
    float distance = glm::length(camera.Position - windows[i]);
    sorted[distance] = windows[i];
}
//这次在渲染的时候,我们将以逆序(从远到近)从map中获取值,之后以正确的顺序绘制对应的窗户
for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it) 
{
    model = glm::mat4();
    model = glm::translate(model, it->second);              
    shader.setMat4("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

在这里插入图片描述

https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/03%20Blending/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值