版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、OpenGL核心技术之混合技术本章给读者介绍关于混合技术的实现,混合在游戏中经常使用,它在引擎中的实现主要是分为三种:透明,半透明,次序无关透明度,本篇博文主要是围绕它们进行。在OpenGL中,物体透明技术通常被叫做混合(Blending)。一个物体的透明度,被定义为它的颜色的alpha值。alpha颜色值是一个颜色向量的第四个元素。美术在制作的游戏图片颜色,主要是由rgba四位组成的,颜色的最后一位就是我们说的alpha通道,它主要是决定材质的透明度的。先说透明的材质处理,做过3D游戏的开发者都比较熟悉,在3D场景编辑器中经常需要在地面上刷一些草,这些草的图片制作是带有alpha通道的,效果
2、如下所示:程序的处理方式就是把背景去掉,把草显示出来,所以,当向场景中添加像这样的纹理时,我们不希望看到一个方块图像,而是只显示实际的纹理像素,剩下的部分可以被看穿。我们要忽略(丢弃)纹理透明部分的像素,不必将这些片段储存到颜色缓冲中。接下来要做的事情就是加载带有Alpha通道的纹理图片,在这里我们使用了SOIL库, SOIL 是一个用于向OpenGL中SOIL库提供的加载函数如下:cpp view plain copy 在CODE上查看代码片派生到我的代码片unsigned char * image = SOIL_load_image(path, &width, &height, 0, SO
3、IL_LOAD_RGBA); 不要忘记还要改变OpenGL生成的纹理:cpp view plain copy 在CODE上查看代码片派生到我的代码片glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); 保证你在片段着色器中获取了纹理的所有4个颜色元素,而不仅仅是RGB元素:cpp view plain copy 在CODE上查看代码片派生到我的代码片void main() / color = vec4(vec3(texture(texture1, TexCoord
4、s), 1.0); color = texture(texture1, TexCoords); 透明材质就加载完成了,接下来就是对草进行摆放了,代码段如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片vector vegetation; vegetation.push_back(glm:vec3(-1.5f, 0.0f, -0.48f); vegetation.push_back(glm:vec3( 1.5f, 0.0f, 0.51f); vegetation.push_back(glm:vec3( 0.0f, 0.0f, 0.7f); vegetatio
5、n.push_back(glm:vec3(-0.3f, 0.0f, -2.3f); vegetation.push_back(glm:vec3( 0.5f, 0.0f, -0.6f); 一个单独的四边形被贴上草的纹理,这并不能完美的表现出真实的草,但是比起加载复杂的模型还是要高效很多,利用一些小技巧,比如在同一个地方添加多个不同朝向的草,还是能获得比较好的效果的。由于草纹理被添加到四边形物体上,我们需要再次创建另一个VAO,向里面填充VBO,以及设置合理的顶点属性指针。在我们绘制完地面和两个立方体后,我们就来绘制草叶:cpp view plain copy 在CODE上查看代码片派生到我的代码
6、片glBindVertexArray(vegetationVAO); glBindTexture(GL_TEXTURE_2D, grassTexture); for(GLuint i = 0; i vegetation.size(); i+) model = glm:mat4(); model = glm:translate(model, vegetationi); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm:value_ptr(model); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray
7、(0); 运行程序得到的效果如下所示:出现这种情况是因为OpenGL默认是不知道如何处理alpha值的,不知道何时忽略(丢弃)它们。我们不得不手动做这件事。幸运的是这很简单,感谢着色器,GLSL为我们提供了discard命令,它保证了片段不会被进一步处理,这样就不会进入颜色缓冲。有了这个命令我们就可以在片段着色器中检查一个片段是否有在一定的阈限下的alpha值,如果有,那么丢弃这个片段,就好像它不存在一样:cpp view plain copy 在CODE上查看代码片派生到我的代码片#version 330 core in vec2 TexCoords; out vec4 color; uni
8、form sampler2D texture1; void main() vec4 texColor = texture(texture1, TexCoords); if(texColor.a 0.1) discard; color = texColor; 在这儿我们检查被采样纹理颜色包含着一个低于0.1这个阈限的alpha值,如果有,就丢弃这个片段。这个片段着色器能够保证我们只渲染哪些不是完全透明的片段。现在我们来看看效果:下面把Shader脚本的顶点着色器给读者展示如下:cpp view plain copy 在CODE上查看代码片派生到我的代码片#version 330 core lay
9、out (location = 0) in vec3 position; layout (location = 1) in vec2 texCoords; out vec2 TexCoords; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() gl_Position = projection * view * model * vec4(position, 1.0f); TexCoords = texCoords; 片段着色器代码如下所示:cpp view plain copy 在CODE上查
10、看代码片派生到我的代码片#version 330 core in vec2 TexCoords; out vec4 color; uniform sampler2D texture1; void main() vec4 texColor = texture(texture1, TexCoords); if(texColor.a 0.1) discard; color = texColor; 其次介绍半透明处理,以上Shader完成了透明材质的渲染,其实这种方法在材质渲染中经常使用,可以把不需要的颜色放弃掉,这种方式不适合渲染半透明的图片,也没有用到Blend混合模式。为了渲染出不同的透明度级别
11、,需要开启混合(Blending),开启混合功能函数如下:cpp view plain copy 在CODE上查看代码片派生到我的代码片glEnable(GL_BLEND); 开启混合后,我们还需要告诉OpenGL它该如何混合。OpenGL以下面的方程进行混合:Csource:源颜色向量。这是来自纹理的本来的颜色向量。Cdestination:目标颜色向量。这是储存在颜色缓冲中当前位置的颜色向量。Fsource:源因子。设置了对源颜色的alpha值影响。Fdestination:目标因子。设置了对目标颜色的alpha影响。片段着色器运行完成并且所有的测试都通过以后,混合方程才能自由执行片段的颜
12、色输出,当前它在颜色缓冲中(前面片段的颜色在当前片段之前储存)。源和目标颜色会自动被OpenGL设置,而源和目标因子可以让我们自由设置。我们来看一个简单的例子:我们有两个方块,我们希望在红色方块上绘制绿色方块。红色方块会成为目标颜色(它会先进入颜色缓冲),我们将在红色方块上绘制绿色方块。那么问题来了:我们怎样来设置因子呢?我们起码要把绿色方块乘以它的alpha值,所以我们打算把FsrcFsrc设置为源颜色向量的alpha值:0.6。接着,让目标方块的浓度等于剩下的alpha值。如果最终的颜色中绿色方块的浓度为60%,我们就把红色的浓度设为40%(1.0 0.6)。所以我们把Fdestinati
13、on 设置为1减去源颜色向量的alpha值。方程将变成:最后的颜色被储存到颜色缓冲中,取代先前的颜色。这个方案不错,但我们怎样告诉OpenGL来使用这样的因子呢?恰好有一个叫做glBlendFunc的函数。void glBlendFunc(GLenum sfactor, GLenum dfactor)接收两个参数,来设置源(source)和目标(destination)因子。OpenGL为我们定义了很多选项,我们把最常用的列在下面。注意,颜色常数向量Cconstant可以用glBlendColor函数分开来设置。为从两个方块获得混合结果,我们打算把源颜色的alphaalpha给源因子,1alp
14、ha1alpha给目标因子,调整到glBlendFunc之后就像这样:cpp view plain copy 在CODE上查看代码片派生到我的代码片glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 也可以为RGB和alpha通道各自设置不同的选项,使用glBlendFuncSeperate:cpp view plain copy 在CODE上查看代码片派生到我的代码片glBlendFuncSeperate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ZERO); 这个方程就像我们之前设置的那
15、样,设置了RGB元素,但是只让最终的alpha元素被源alpha值影响到。OpenGL给了我们更多的自由,我们可以改变方程源和目标部分的操作符。现在,源和目标元素已经相加了。如果我们愿意的话,我们还可以把它们相减。void glBlendEquation(GLenum mode)允许我们设置这个操作,有3种可行的选项:GL_FUNC_ADD:默认的,彼此元素相加:GL_FUNC_SUBTRACT:彼此元素相减: GL_FUNC_REVERSE_SUBTRACT:彼此元素相减,但顺序相反:通常我们可以简单地省略glBlendEquation因为GL_FUNC_ADD在大多数时候就是我们想要的,但
16、是如果你如果你真想尝试努力打破主流常规,其他的方程或许符合你的要求。现在我们知道OpenGL如何处理混合,是时候把我们的知识运用起来了,我们来添加几个半透明窗子。首先,初始化时我们需要开启混合,设置合适和混合方程:cpp view plain copy 在CODE上查看代码片派生到我的代码片glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 由于我们开启了混合,就不需要丢弃片段了,所以我们把片段着色器设置为原来的那个版本:cpp view plain copy 在CODE上查看代码片派生到我的代码片#ve
17、rsion 330 core in vec2 TexCoords; out vec4 color; uniform sampler2D texture1; void main() color = texture(texture1, TexCoords); 它根据alpha值,把当前片段的颜色和颜色缓冲中的颜色进行混合。因为窗子的玻璃部分的纹理是半透明的,我们应该可以透过玻璃看到整个场景。如果你仔细看看,就会注意到有些不对劲。前面的窗子透明部分阻塞了后面的。为什么会这样?原因是深度测试在与混合的一同工作时出现了点状况。当写入深度缓冲的时候,深度测试不关心片段是否有透明度,所以透明部分被写入深度缓
18、冲,就和其他值没什么区别。结果是整个四边形的窗子被检查时都忽视了透明度。即便透明部分应该显示出后面的窗子,深度缓冲还是丢弃了它们。所以我们不能简简单单地去渲染窗子,我们期待着深度缓冲为我们解决这所有问题;这也正是混合之处代码不怎么好看的原因。为保证前面窗子显示了它后面的窗子,我们必须首先绘制后面的窗子。这意味着我们必须手工调整窗子的顺序,从远到近地逐个渲染。这里要注意:对于全透明物体,比如草叶,我们选择简单的丢弃透明像素而不是混合,这样就减少了令我们头疼的问题(没有深度测试题)。下面介绍如何按照顺序渲染物体,要让混合在多物体上有效,我们必须先绘制最远的物体,最后绘制最近的物体。普通的无混合物体
19、仍然可以使用深度缓冲正常绘制,所以不必给它们排序。我们一定要保证它们在透明物体前绘制好。当无透明度物体和透明物体一起绘制的时候,通常要遵循以下原则:先绘制所有不透明物体。 为所有透明物体排序。 按顺序绘制透明物体。 一种排序透明物体的方式是,获取一个物体到观察者透视图的距离。这可以通过获取摄像机的位置向量和物体的位置向量来得到。接着我们就可以把它和相应的位置向量一起储存到一个map数据结构(STL库)中。map会自动基于它的键排序它的值,所以当我们把它们的距离作为键添加到所有位置中后,它们就自动按照距离值排序了:cpp view plain copy 在CODE上查看代码片派生到我的代码片st
20、d:map orted; for (GLuint i = 0; i windows.size(); i+) / windows contains all window positions GLfloat distance = glm:length(camera.Position - windowsi); sorteddistance = windowsi; 最后产生了一个容器对象,基于它们距离从低到高储存了每个窗子的位置。随后当渲染的时候,我们逆序获取到每个map的值(从远到近),然后以正确的绘制相应的窗子:cpp view plain copy 在CODE上查看代码片派生到我的代码片for(
21、std:map:reverse_iterator it = sorted.rbegin(); it != sorted.rend(); +it) model = glm:mat4(); model = glm:translate(model, it-second); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm:value_ptr(model); glDrawArrays(GL_TRIANGLES, 0, 6); 我们从map得来一个逆序的迭代器,迭代出每个逆序的条目,然后把每个窗子的四边形平移到相应的位置。这个相对简单的方法对透明物体进行了排序,
22、修正了前面的问题,现在场景看起来像这样:虽然这个按照它们的距离对物体进行排序的方法在这个特定的场景中能够良好工作,但它不能进行旋转、缩放或者进行其他的变换,奇怪形状的物体需要一种不同的方式,而不能简单的使用位置向量。在场景中排序物体是个有难度的技术,它很大程度上取决于你场景的类型,更不必说会耗费额外的处理能力了。完美地渲染带有透明和不透明的物体的场景并不那么容易。接下介绍技术次序无关透明度,为了解决上述提到的问题,我们使用更高级的更高级的技术更高级的技术次序无关透明度。下面介绍该技术的实现,我们的渲染主要分两步:第一步:渲染填充链表;第二步:渲染排序+blend; 先看第一步,我们使用片段着色
23、器来填充链表:cpp view plain copy 在CODE上查看代码片派生到我的代码片#version 420 core layout (early_fragment_tests) in; layout (binding = 0, r32ui) uniform uimage2D head_pointer_image; layout (binding = 1, rgba32ui) uniform writeonly uimageBuffer list_buffer; layout (binding = 0, offset = 0) uniform atomic_uint list_coun
24、ter; layout (location = 0) out vec4 color; in vec4 surface_color; uniform vec3 light_position = vec3(40.0, 20.0, 100.0); void main(void) uint index; uint old_head; uvec4 item; index = atomicCounterIncrement(list_counter); old_head = imageAtomicExchange(head_pointer_image, ivec2(gl_FragCoord.xy), uin
25、t(index); item.x = old_head; item.y = packUnorm4x8(surface_color); item.z = floatBitsToUint(gl_FragCoord.z); item.w = 255 / 4; imageStore(list_buffer, int(index), item); /color = surface_color; discard; 同时把顶点着色器代码也给读者展示一下:cpp view plain copy 在CODE上查看代码片派生到我的代码片#version 330 layout (location = 0) in v
26、ec3 position; layout (location = 1) in vec3 normal; uniform mat4 model_matrix; uniform mat4 view_matrix; uniform mat4 projection_matrix; uniform float minAlpha = 0.5f; out vec4 surface_color; void main(void) vec3 color = normal; if (color.r 0) color.r = -color.r; if (color.g 0) color.g = -color.g; i
27、f (color.b 0) color.b = -color.b; vec3 normalized = normalize(color); float variance = (normalized.r - normalized.g) * (normalized.r - normalized.g); variance += (normalized.g - normalized.b) * (normalized.g - normalized.b); variance += (normalized.b - normalized.r) * (normalized.b - normalized.r);
28、variance = variance / 2.0f;/ range from 0.0f - 1.0f float a = (0.75f - minAlpha) * (1.0f - variance) + minAlpha; surface_color = vec4(normalized, a); gl_Position = projection_matrix * view_matrix * model_matrix * vec4(position, 1.0f); 下面开始第二步骤的操作是渲染排序+blend,片段着色器代码如下所示:cpp view plain copy 在CODE上查看代码
29、片派生到我的代码片#version 420 core / The per-pixel image containing the head pointers layout (binding = 0, r32ui) uniform uimage2D head_pointer_image; / Buffer containing linked lists of gments layout (binding = 1, rgba32ui) uniform uimageBuffer list_buffer; / This is the output color layout (location = 0)
30、out vec4 color; / This is the maximum number of overlapping fragments allowed #define MAX_FRAGMENTS 40 / Temporary array used for sorting fragments uvec4 fragment_listMAX_FRAGMENTS; void main(void) uint current_index; uint fragment_count = 0; current_index = imageLoad(head_pointer_image, ivec2(gl_FragCoord).xy).x; while (current_index != 0 & fragment_count 1) for (u
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 下游无纺布医疗卫材需求爆发对锦纶短纤产能消化的拉动
- 一体化压铸工艺迭代对分体式铸造机件资产价值的挤出效应
- Z世代书写体验经济驱动非直液式走珠笔情绪价值资本化路径
- 2026年上海东海职业技术学院高职单招笔试职业适应性测验试题库含答案解析3套试卷
- 2026山西省住院医师规范化培训结业理论考核(康复医学科)历年参考题库含答案详解
- 2026导游资格证-地方导游基础知识考试历年参考题库含答案详解
- 2026国企招聘考试(面试-电气工程)历年参考题库含答案详解
- 2026四川省建筑施工企业安管人员考试(专职安全生产管理人员·C3类)历年参考题库含答案详解
- 2026吉林机关事业单位工人技术等级考试(中级美发师)历年参考题库含答案详解
- 2026卫生专业技术资格考试(血液病学)历年参考题库含答案详解
- 2026上海闵行区机关事业单位编外人员招聘笔试参考题库及答案详解
- 2026成人高考高起专数学模拟练习试题及答案
- 2026年成都华西中学初一入学数学分班考试真题含答案
- 2026浙江杭州西湖区市场监督管理局招聘编外合同制工作人员3人笔试参考题库及答案详解
- 人工智能训练师(高级技师)职业技能资格理论备考题库
- 2026年上海市春季高考语文真题试卷及答案(详解版)
- (2026年)重症后管理专家共识应用经验分享学习与解读课件
- 中国泌尿系结石临床诊疗指南(2025版)
- 上海八年级数学上册-一元二次方程的应用-实际问题(同步练习)
- 2026年全市统计基本单位名录库题库
- 2026年北京市海淀区高三一模生物试卷(含答案)
评论
0/150
提交评论