1. 流程

OpenGL中,混合(Blending)通常是实现物体透明度(Transparency)的一种技术。透明就是说一个物体(或者其中的一部分)不是纯色(Solid Color)的,它的颜色是物体本身的颜色和它背后其它物体的颜色的不同强度结合。这在我们之前的教程中其实有过类似的功能,就是在片段着色器中对两个纹理的颜色进行mix,使得最终呈现出来的颜色是两个纹理不同程度的混合:

而这次我们可以用一种更通用的方法,那就是alpha测试

1.1 Alpha测试

在我们读取图片的时候,有的图片数据除了RGB通道之外,还包含了Alpha通道,我们生成该纹理的时候可以设置为GL_RGBA来读取:

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

有了这个Alpha参数,我们可以怎么用呢?

同样看到之前混合纹理时用的片段着色器:

FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), visibility);

我们使用了一个mix函数,使得两个纹理根据一个0-1之间的值visibility来分别设定它们的可见度,而这个visibility其实就可以看作我们读取的Alpha值,为0时原图片完全透明(着色时不使用它的颜色),为1时原图片完全不透明(着色时完全使用它的颜色)

现在我们想要实现一种效果,我们要在游戏场景中渲染一块草地,只用一个贴图,如下图:

直接将它渲染在场景中可能会是这样:

这是因为空白部分的纯色纹理也被渲染出来了,现在我们只要这个图片中草的片段被渲染出来怎么办?

这时候就要用到之前读取的图片alpha值了,在读取的时候,空白部分的alpha值被设定为0,我们可以在片段着色器里这样设置:

#version 330 core
out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture1;void main()
{             vec4 texColor = texture(texture1, TexCoords);if(texColor.a < 0.1)discard;FragColor = texColor;
}

没错,GLSL提供了一个discard功能,在我们读取纹理值,发现它的alpha值小于0.1时,我们就调用discard丢弃这个片段而不着色,这样就能够只渲染草片段的纹理了:

这只是一种简单的应用,实际应用时,我们还更可能碰到一种情况,就是在渲染半透明物体时可能需要将物体本身的纹理与背景的颜色混合,而使用mix函数肯定无法完成这种较为复杂的操作,这时候就不得不提到OpenGL的另一个功能了

1.2 混合测试

在OpenGL中,也提供了一种类似于mix的方法,那就是混合(Blend)

我们在开始渲染时,需要开启这个功能:

glEnable(GL_BLEND);

然后,我们需要使用一个函数来指定混合效果:

glBlendFunc(GLenum sfactor, GLenum dfactor);

它的参数有这些类型:

对于一般的mix效果(使用源颜色向量的alpha作为源因子,使用1−alpha作为目标因子),我们可以这样设置:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

我们还可以通过glBlendEquation(GLenum mode)来设置运算符达到其他效果(默认是两个颜色相加):

1.3 半透明

接下来,我们就来渲染一个有色玻璃:

首先,在初始化时我们启用混合,并设定相应的混合函数:

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

由于启用了混合,我们就不需要丢弃片段了,所以我们把片段着色器还原:

#version 330 core
out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture1;void main()
{             FragColor = texture(texture1, TexCoords);
}

效果:

发现有一个问题,前面的玻璃怎么把后面的玻璃遮挡住了?

这是因为开启了深度测试的原因,如果我们从近到远渲染玻璃的话,后面玻璃被遮挡的部分就不会渲染了,所以这个是比较麻烦的事,一般情况下,我们每次渲染半透明物体的时候,都是把它们按照与相机的距离从后到前渲染,我们可以利用map存储玻璃,键值设为距离,这样它就会自动按照距离存储了:

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的一个反向迭代器(Reverse Iterator),反向遍历其中的条目,实现从远到近

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);
}

1.4 深度测试、模板测试、Alpha测试和混合测试

那么我们最后结合深度测试、模板测试、Alpha测试和混合测试来一遍

首先开启深度和Alpha测试

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

然后渲染箱子和灯两类不透明物体:

先渲染灯

    while (!glfwWindowShouldClose(window)){// 输入processInput(window);//调用glClear函数,清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);// 渲染指令float timeValue = glfwGetTime();deltaTime = timeValue - lastFrame;lastFrame = timeValue;view = projection = glm::mat4(1.0f);projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);view = camera.GetViewMatrix();light.view = view;light.projection = projection;light.Draw(lightShader);

接着开启模板测试渲染箱子,让模板缓存先写入:

        glEnable(GL_STENCIL_TEST);glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);glStencilFunc(GL_ALWAYS, 1, 0xFF);glStencilMask(0xFF);box.view = view;box.projection = projection;box.Draw(ourShader, camera, timeValue);

之后我们关闭模板测试,开始从远到近绘制半透明的玻璃:

        glDisable(GL_STENCIL_TEST);glass.view = view;glass.projection = projection;glass.Draw(glassShader, camera);

最后我们开启模板测试,绘制箱子边框:

        glEnable(GL_STENCIL_TEST);glStencilFunc(GL_NOTEQUAL, 1, 0xFF);//glStencilMask(0x00);glDisable(GL_DEPTH_TEST);box.Draw(singleShader, camera, timeValue, 1.05f);glEnable(GL_DEPTH_TEST);glDisable(GL_STENCIL_TEST);glfwSwapBuffers(window);glfwPollEvents();}

之所以先渲染不透明再渲染半透明,是因为半透明的物体颜色需要混合背景的不透明物体

而之所以要在第一遍渲染箱子之后关闭模板测试,是为了不让渲染玻璃时影响模板缓存

效果(正面):

效果(反面):

1.5 测试顺序


alpha的操作会在multisample fragment operations里面

也就是测试顺序是:裁剪 Alpha 模板 深度 混合

【OpenGL】笔记二十一、Alpha测试、混合测试相关推荐

  1. python3.4学习笔记(二十一) python实现指定字符串补全空格、前面填充0的方法

    python3.4学习笔记(二十一) python实现指定字符串补全空格.前面填充0的方法 Python zfill()方法返回指定长度的字符串,原字符串右对齐,前面填充0. zfill()方法语法: ...

  2. 数据科学和人工智能技术笔记 二十一、统计学

    二十一.统计学 作者:Chris Albon 译者:飞龙 协议:CC BY-NC-SA 4.0 贝塞尔校正 贝塞尔的校正是我们在样本方差和样本标准差的计算中使用 n−1n-1n−1 而不是 nnn 的 ...

  3. Mr.J-- jQuery学习笔记(二十一)--模拟微博页面

    先看之前的节点操作方法:Mr.J-- jQuery学习笔记(二十)--节点操作方法 Mr.J-- jQuery学习笔记(五)--属性及属性节点 Mr.J-- jQuery学习笔记(十一)--事件委托  ...

  4. 【Visual C++】游戏开发笔记二十一 游戏基础物理建模(三) 摩擦力系统模拟

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/7555785 作者:毛星云    邮箱: h ...

  5. 【Visual C++】游戏开发笔记二十一 游戏基础物理建模 三 摩擦力系统模拟

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  6. RV1126笔记二十一:车辆颜色识别

    若该文为原创文章,转载请注明原文出处. 一.介绍 在学习RV1126的过程中,测试了yolov5可以实现物体检测,物体目标识别等功能,Rock-X也自带了车牌识别功能,具体可以了解下正点原子的资料,里 ...

  7. kvm虚拟化学习笔记(二十一)之KVM性能优化学习笔记

    本学习笔记系列都是采用CentOS6.x操作系统,KVM虚拟机的管理也是采用virsh方式,网上的很多的文章都基于ubuntu高版本内核下,KVM的一些新的特性支持更好,本文只是记录了CentOS6. ...

  8. IOS学习笔记二十一(NSDictionary、NSMutableDictionary)

    1.NSDictionary.NSMutableDictionary 可以理解为java里面的map,一个key对应一个value,key不可以重复 NSDictionary不可变,NSMutable ...

  9. 设计模式笔记二十一:状态模式

    原文:http://www.runoob.com/design-pattern/ 少许个人理解,如有错误请指出.欢迎一起讨论.(本文多摘自原文) 在状态模式(State Pattern)中,类的行为是 ...

  10. 【OpenGL】二十一、OpenGL 矩阵压栈与出栈 ( 不同类型矩阵变换先后顺序 | 渲染前不设置单位阵 | 压栈出栈原理分析 | 代码示例 )

    文章目录 一.不同类型矩阵变换先后顺序 二.渲染前不设置单位阵 三.矩阵的压栈和出栈原理分析 四.矩阵的压栈和出栈代码示例 五.相关资源 一.不同类型矩阵变换先后顺序 对 OpenGL 中的 模型视图 ...

最新文章

  1. 深度丨人工智能和大数据的关系及中国在AI领域如何赶超世界
  2. js控制鼠标移动_原生JS封装可拖拽效果
  3. 构建iOS稳定应用架构时方案选择的思考,主要涉及工程结构,数据流思想和代码规范...
  4. STM32 单片机启动流程
  5. java中怎样验证重复文件_java – 如何在下载之前检查URL中的重复文件
  6. vue自定义指令(详细)
  7. 【学习】如何用jQuery获取iframe中的元素
  8. java并发编程(4)--单例模式的安全问题 volatile
  9. 凸优化第二章凸集 2.4广义不等式
  10. java删除文件和文件夹
  11. 单片机c语言入门自学,单片机C语言入门教程
  12. vs 2017 查看dll源代码
  13. Excel进行数据分析的常用知识的学习整理
  14. 乳酸的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  15. [NN]前向神经网络的tf.keras详细实现教学
  16. 快速上手IntelliJ IDEA常用快捷键
  17. 图解Oracle数据库(二)
  18. 优盘里文件夹变成html,u盘里面的文件打开里面没内容了怎么回事 u盘打开之后里面的文件都不见了怎么办...
  19. iOS-地图真实坐标表示形式之间转换(double型,int型 互转)
  20. 创业前期如何选择云服务器?

热门文章

  1. [Python] 年终奖税后计算器
  2. LaTeX系统找不到指定文件解决方案
  3. AD域控与ISA防火墙控制上网
  4. win10安装atom的步骤
  5. 通达信大资金进出指标公式
  6. itextpdf加盖章
  7. 云计算基础架构即服务、平台即服务、软件即服务的三种服务类型的介绍
  8. Error running ‘Tomcat8.5‘ port out of range-1 (moments ago)
  9. do vis是什么意思_duck不必什么梗?李佳琦放过鸭子吧表情包
  10. 爬虫学习分享Python下载mp4文件(源码)