OpenGL蓝宝书第九章学习笔记:片段着色器和帧缓存

前言
本篇在讲什么

OpenGL蓝宝书第九章学习笔记之片段着色器和帧缓存
本篇适合什么

适合初学OpenGL的小白
本篇需要什么

C++语法有简单认知
OpenGL有简单认知
最好是有OpenGL超级宝典蓝宝书
依赖Visual Studio编辑器

本篇的特色

具有全流程的图文教学
重实践,轻理论,快速上手
提供全流程的源码内容


★提高阅读体验★

👉 ♠ 一级标题 👈

👉 ♥ 二级标题 👈

👉 ♣ 三级标题 👈

👉 ♦ 四级标题 👈


♠ 本章的学习要点

  • 如何将数据传递到片段着色器,如何控制数据发送给片段着色器的方式以及片段着色器后如何处理数据
  • 如何创建自己的帧缓存并控制它们存储的数据格式
  • 如何从单个片段着色器生成多个输出
  • 如何获取帧缓冲数据并将数据输入纹理、缓冲以及应用程序的内存

♠ 片段着色器

片段着色器确定每个片段的颜色,然后组合发送给帧缓冲
默认情况下,片段着色器的所有输入块都平滑地在经过光栅化的基元上进行插值,该插值的端点由前端最后一个阶段提供(顶点着色器或曲面细分阶段)
你可以在很大程度上控制插值方式甚至决定是否插值


♥ 插值与存储限定符

GLSL支持的一些存储限定符,有一些存储限定符可用于控高级渲染时可使用的插值


♣ 禁用差值

每当从前端传递一个整数到后端时,就必须禁用插值,这一设置是自动完成的,因为 OpenGL王法平滑地插值整数

要为未插的片段着色器创建平场输入,可以使用’flat’存储限定符进行声明

flat in vec4 foo;
flat in int bar;
flat in mat3 baz;

可以将插值限定符应用于输入块
可以对输入块的各元素应用不同的限定符

flat in INPUT BLOCK
{
    vec4 foo;
    int  bar;
    smooth mat3 baz;
}

上述代码foo已禁用插值,因为它从父输入块继承了flat限定
bar是一个整数,因此自动禁用插值
baz虽然父块是禁用限定符flat,但是因为因为限定符smooth存在,所以依旧是平滑插值

当渲染基元为线条或三角形时,则使用基元的第一个或最后一个顶点,可以通过调用以下函数决定

void glProvokingVertex(GLenum provokeMode);

♣ 无透视校正的插值

因为空间是立体的,所以三角形对于视线来说很少是直接垂直的,这意味屏幕空间内的步长不是线性相关,OpenGL通过使用透视校正插值来纠正这个问题

如果要在屏幕空间中执行插值而不考虑基元方向,则可使用noperspective存储限定符


♠ 单片段测试

片段着色器运行后,会对片段进行了许多其他测试,以确定是否以及如何将其写入帧缓存。包括剪裁测试模板测试以及深度测试


♥ 剪裁测试

剪裁测试默认是禁用的,OpenGL支持很多剪裁矩形。如果要设置这些矩形则可调用glscissorIndexed()glScissorIndexedv(),其原型为

void glScissorIndexed(GLuint indexGLint left,GLint bottom,GLsizei width,GLsizei height)

void glScissorIndexedv(GLuint index,const GLint * v)

可以通过调用以下函数设置每个剪裁工具的矩形,无论OpenGL实现支持多大的数量

void glScissor(GLint x, GLint y,GLsizei width,GLsizei height);

全域启动裁剪测试

glEnable(GL_SCISSOR_TEST)

禁用剪裁测试

glDisable(GL_SCISSOR_TEST)

单个视口矩形启用剪裁测试

glEnablei(GL_SCISSOR_TEST,index);

单个视口矩形禁用剪裁测试

glDisablei(GL SCISSOR_TEST,index);

♥ 模板测试

简单理解,用镂空的纸板在墙上喷漆,只有镂空的位置会形成图案

启用模板测试

glEnable(GL_STENCIL_TEST)

glstencilFuncSeparate()该命令用于控制模板测试通过或不通过的条件,其原型如下

void glStencilFuncSeparate(GLenum faceGLenum func,GLint ref,GLuint mask);

可为face传递GL_FRONT、GL_BACK或GL_FRONTAND_BACK,放大将受到影响的几何体
func的值可以是下表中的任意值,这些值指定了几何体通过模板测试的条件

功能通过条件
GL_BQUAL参考值等于缓冲值
GL_GEQUAL参考值大于或等于缓冲值
GL_GREATER参考值大于缓冲值
GL_NOTEQUAL参考值不等于缓冲值
GL_EQUAL参考值等于缓冲值
GL_GEQUAL参考值大于或等于缓冲值
GL_GREATER参考值大于缓冲值
GL_NOTEQUAL参考值不等于缓冲值

ref值是用于计算结果通过或失败的引用,mask参数可以控制将哪些引用位与缓存进行比较

glstencilopSeparate()告诉OpenGL在模板测试通过或失败后应如何处理,其原型如下

void glstencilOpSeparate(GLenum face,GLenum sfail,GLenum dpfail,GLenum dppass)

参数face指定哪些面会受到影响。接下来的3个参数控制执行模板测试后发生的事情,可以是下表中的任意值

功能结果
GL_KEEP不修改模板缓冲
GL_ZERO将模板缓冲值设置为0
GL_REPLACE将模板值替换成参考值
GL_INCR带饱和度的增量模板
GL_DECR带饱和度的减量模板
GL_INVERT按位倒转模板值
GL_INCR_WRAP无饱和度的增量模板
GL_DECR_WRAP无饱和度的减量模板

sfail是模板测试失败后行的操作
dpfail参数指定深度缓冲测试失败后执行的操作
dppass指定深度冲测试通过后执行的操作


♥ 深度测试

启用深度测试

glEnable(GL_DEPTH_TEST)

禁用深度测试

glDisable(GL_DEPTH_TEST)

要设置深度比较运算符(或深度函数),则调用glDepthFunc(),其原型为

void glDepthFunc(GLenum func);

参数func是一种可用的深度比较运算符,func的选择可见下表

功能含义
GL_ALWAYS深度测试始终通过一所有片段均视为已通过深度测试
GL_NEVER深度测试从未通过一一所有片段均视为未通过深度测试
GL_LESS如果新片段的深度值小于旧片段的深度值,则通过深度测试
GL_LEQUAL如果新片段的深度值小于或等于旧片段的深度值,则通过深度测试
GL_EQUAL如果新片段的深度值等于旧片段的深度值,则通过深度测试
GL_NOTEQUAL如果新片段的深度值不等于旧片段的深度值,则通过深度测试
GL_GREATER如果新片段的深度值大于旧片段的深度值,则通过深度测试
GL_GEQUAL如果新片段的深度值大于或等于旧片段的深度值,则通过深度测试

♣ 控制深度缓冲更新

glDepthMask()函数空值深度缓存的写入,GL_TRUE则启用,GL_FALSE则禁用

glDepthMask(GL_FALSE);

♣ 深度夹紧

OpenGL可以选择关闭对近平面和远平面的裁剪,将生成的深度值限制在0~1

启用深度夹紧

glEnable(GL_DEPTH_CLAMP)

禁用深度夹紧

glDisable(GL_DEPTH_CLAMP)

♠ 颜色输出

颜色输出阶段是片段被写入帧缓存前所经历的最后一个openGL管线阶段,它确定了颜色数据离开片段着色器后最终显示给用户之前所经历的操作


♥ 混合

就是颜色叠加,几乎所有的动画或者游戏引擎都支持颜色叠加效果

启用混合

glEnable(GL_BLEND);

禁用混合

glDisable(GL BLEND);

♣ 混合函数

可以调用gIBlendFunc()glBlendFuncSeparate()函数,其函数原型如下

glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,GLenum srcAlpha, GLenum dstaAlpha) ;
glBlendFunc(GLenum src, GLenum dst);

最后一个值常量混色可通过调用 glBlendColor()设置


♥ 逻辑运算

在传递像素颜色前可以先对其应用一次逐辑运算

启用逻辑运算

glEnable(GL_COLOR_LOGIC_OP);

禁用逻辑运算

glDisable(GL_COLOR_LOGIC_OP);

逻运算使用输入像素和现有帧缓冲的值计算最终值,可通过调用glLogicop()来选择计算最终值的操作


♥ 颜色遮罩

写入片段前可以对其进行的最后修改之一是遮罩
为了应用颜色遮罩或禁用颜色遮,可以使用gColorMask()gcolorMaski()函数
函数g1ColorMask()允许你屏蔽当前已启用的所有演染缓存
函数glcolorMaski()许你将罩设置成特定颜色缓存


♠ 离屏渲染

OpenGL包含用户可自行设置帧缓存并用于直接绘制到纹理的功能

OpenGL中的大多数对象,可使用适当的创建函数glCreateFramebuffers()创建一个或多个缓存对象

  • 生成帧缓存对象的名称
void glGenFramebuffers(GLsizei n,GLuint * framebuffers);
  • 要将缓存绑定到语境
void glBindFramebuffer(GLenum target,GLuint framebuffer)
  • 附加纹理到帧缓存
void glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment,GLuint texture,GLint level);
void glFramebufferTexture(GLenum target,GLenum attachment,GLuint texture,GLint level);

♥ 多个帧缓冲附件

用户定义帧缓存的另一个极其有用的功能是,它们支持多个附件,也就是说,可将多个纹理附加到单个帧缓存并同时使用单个片段着色器渲染到其中

纹理附加到FBO,我们需要调用glFramebufferTexture()gINamedFramebufferTexture()函数

要从单个片段着色器渲染到多个附件,则必须在着色器中声明多个输出并将其关联到附着点。为此,我们使用布局限定符,如下所示

layout (location = 0) out vec4 color0;
layout (location = 1) out vec4 colorl;
layout (location = 2) out vec4 color2;

绘制使用函数glDrawBuffers()(复数)函数


♥ 分层渲染

分层排列的可索引至着色器的2D纹理,也可将纹理附加到缓存对象,这种帧缓存就叫作分层缓存


♥ 帧缓存的完整性

完整性分为两类:附件完整性全帧缓存完整性


♣ 附件完整性

FBO的每个附件必须满足某些标准才能视为完整

  • 附件对象无任何相关图像
  • 附加图像的宽度或高度为零
  • 将非彩色可渲染格式附加到彩色附件
  • 将非深度可渲染格式附加到深度附件
  • 将非模板可渲染格式附加到模板附件

要确定颜色深度或模板格式是否可渲染,使用下面函数,结果为GL_TRUE或GL_FALSE

glGetInternalformativ(GL_COLOR_RENDERABLE)   //(颜色)
glGetInternalformativ(GL_DEPTH_RENDERABLE)   //(深度)
glGetInternalformativ(GL_STENCIL_RENDERABLE) //(模板)

♣ 全帧缓存完整性

帧缓存对象作为一个整体也必须是完整的,全帧缓存不完整的常见情形如下

  • glDrawBuffers()已将输出映射到一个无附加图像的FBO附件
  • OpenGL驱动器不支持同时使用多种内部格式

♣ 检查帧缓冲

当用户认为已经完成FBO设置时,可以通过调用以下函数检查FBO是否完整

Glenum fbostatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
GLenum fbostatus = glCheckNamedFramebufferStatus (framebuffer);

♥ 立体渲染

OpenGL可根据所用显示设备生成成对的图像,这些图像可分别呈现给两眼并提高图像景深感

  • 渲染到左眼图像
glDrawBuffer(GLBACK_LEFT);
  • 渲染到右眼图像
glDrawBuffer(GL_BACK RIGHT);

♠ 反混叠

混叠就是锯齿锯齿。减少或消除混叠的方法叫作反混叠技术


♥ 过滤法反混叠

启用方式

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

仅适合少数情况,对于立方体,需要把GL_LINE_SMOOTH替换成GL_POLYGON_SMOOH,如下代码所示

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

♥ 多样本反混叠

为增加图像的采样率,OpenGL支持在屏幕上存储每个像素的多个样本。这种技术称为多样本反混叠(MSAA)

启用多采样

glEnable(GL_MULTISAMPLE);

禁用多采样

glDisable(GL_MULTISAMPLE);

♥ 多样本纹理

我们可以创建一个多采样纹理并将其附加到帧缓存对象进行渲染

  • 多采样纹理

例如:GL_TEXTURE_2D_MULTISAMPLE或GL_TEXTURE_2D_MULTISAMPLE_ARRAY

  • 分配储存空间

glTextureStorage2DMultisample()或glTextureStorage3DMultisample()


♣ 样本覆盖率

覆盖率指片段“覆盖”的像素占比

OpenGL将片段透明度值直接转换成覆盖率值,调用glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);

也可以通过调用gl_SampleCoverage()手动设置样本覆盖率

另一种可以生成覆盖信息的方法是在片段着色器中明确设置该信息,使用片段着色器内置变量即gl_SampleMaskIn[]gl_SampleMask[]


♥ 采样率着色

-启用采样率着色

gl_Enable(GL SAMPLE SHADING);

-禁用采样率着色

gl_Disable(GL_SAMPLE_SHADING);

单独对哪部分样本着色,可以调用gl_MinSampleShading()


♥ 重心采样

它仅适用于渲染到多采样帧缓存的情形
要创建带有 centroid 存储限定符的易变变量


♠ 高级帧缓冲格式

适用于帧缓存的更高级的格式


♥ 无附件渲染

可以创建帧缓存但不附加任何纹理

每个帧缓存对象都有一组参数,用于替代无附件时从其附件获得的参数。为了指定这些参数,需要调用gl_FramebufferParameteri()


♥ 浮点帧缓冲

浮点格式的附件


♣ 使用浮点格式

创建缓存时可使用两种新记号,即GL_RGBA16EGL_RGBA32F,如下所示

glTextureStorage2D(texture,1,GL_RGBA16F,width, height);
glTextureStorage2D(texture,1,GL_RGBA32F,width, height);

有许多可用格式如下表所示

格式内容
GL_RGBA32F4个32位浮点分量
GL_RGBA16F4个16位浮点分量
GL_RGB32F3 个32 位浮点分量
GL_RGB16F3个16 位浮点分量
GL_RG32F两个32位浮点分量
GL_RG16F两个 16 位浮点分量
GL_R32F一个32位浮点分量
GL_R16F一个16 位浮点分量
GL_R11F_G11F_B10F两个 11 位浮点分量和一个 10 位浮点分量

♥ 整数帧缓冲

使用正数内部格式创建纹理并附加到帧缓存对象来创建整数帧缓冲

使用包含整数分量的内部格式创建纹理并附加到帧缓存对象(例如,GL RGBA32UI),如下所示

glTextureStorage2D(tex,1,GLRGBA32UI,10241024);

♥ SRGB颜色空间

如果输入电压达到最大电压的-半,则光输出还略低于最大可能光输出的四分之一!为了弥补这一点,在计算机图形中,使用伽马校正(以y项的幂函数命名),通过低幂次提高线性值、缩放结果和补偿结果值。生成的颜色空间叫作 sRGB


♠ 点精灵

术语点精灵(pointsprites)通常指纹理点,指以点的渲染来代替纹理,绘制单个 3D点将2D纹理图像置于屏幕上,常见于粒子系统


♥ 点纹理化

使用点精灵只需要绑定一个2D纹理并使用内置变量gl_PointCoord从片段着色器中读取该纹理,如下述代码所示

#version 450 core
out vec4 vFragColor;

in vec4 vStarColor;

layout (binding =0) uniform sampler2D starImage;

void main(void)
{
    vFragColor = texture(starImage, gl_PointCoord) * vStarColor;
}

♥ 点参数

可以使用函数glPointParameteri()对点精灵(以及一般点)的一些特征进行微调


♥ 有形点

除使用gl_PointCoord对纹理坐标应用纹理外,还可以使用gl_PointCoord导出除纹理坐标外的许多其他信息
例如,可以使用片段着色器中的discard关键词生成非正方形的点,从而舍弃预期点形状外的片段


♥ 旋转点

可以在片段着色器中直接创建旋转矩阵,然后将其与gl_Pointcoord相乘使它旋转


♠ 获取图像

仅仅获取最终的渲染图像,不一定需要向用户展示,例如截图、打印图像等


♥ 从帧缓存中读取

OpenGL提供函数glReadPixels()能从帧缓存中读取像素数据,其原型如下

void glReadPixels(GLint X, GLint y,GLsizei width,GLsizei height, GLenum format, GLenum type,GLvoid * data);


♥ 在帧缓冲之间复制数据

图形API允许应用程序将像素或缓冲数据读取至系统内存,还提供了将这些像素或数据绘制至屏幕的方法

上述方法很低效,可以使用位块传输命令将像素数据从一个点快速移动到另一个点,步骤如下

  • 复制源是通过调用glReadBuffer()指定的读取缓存的读取缓存
  • 复制目标是通过调用glDrawBuffer()指定的当前绘制缓存的绘制缓存
  • 通过调用glReadPixels()将缓存中的数据读入应用程序的内存
  • 使用g1BlitFramebuffer()从一个缓存读入另一个缓存
  • 更简单地将数据直接从缓存复制到纹理中,通过glCopyTexSubImage2D()glcopyTexturesubImage2D()
  • 如果想要将纹理中的数据复制到另一个纹理中,则可以通过调用glCopyImageSubData()来实现

♥ 读取纹理数据

通过调用以下任意函数从纹理中读取图像数据

void glGetTexImage(GLenum targetGLint Ievel,GLenum format, GLenum type,GLvoid *img);
void glGetTextureImage(GLuint texture, Glint level,GLenum format,GLenum type,GLsizei bufsize, void *pixels)

上述函数都将整级纹理读回到内存中,如果只需要一小片纹理,则可调用下面函数

void glGetTextureSubImage (GLuint texture, GLint level,
GLint xoffset,GLint yoffset, GLint zoffset,GLsizei width,GLsizei height, GLsizei depth,GLenum format,GLenum type,GLsizei bufSize, void *pixels);


♠ 推送

  • Github
https://github.com/KingSun5

♠ 结语

若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。

👉 本文属于原创文章,转载请评论留言,并在转载文章头部著名作者出处👈
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OpenGL蓝宝书PDF是指《OpenGL超级宝典》一书的电子版本,主要介绍了OpenGL图形学编程的基础知识和开发技巧。 OpenGL是一种跨平台的图形库,可以用于开发高性能的2D和3D图形应用程序。《OpenGL超级宝典》是一本经典的OpenGL教材,适合初学者和有一定OpenGL基础的开发者学习和参考。 这本书的PDF版本提供了一种便捷的阅读方式,可以随时随地通过电脑、平板或手机进行学习。相比于传统的纸质书籍,PDF版本的《OpenGL超级宝典》具有以下优势: 1. 可随时复制和搜索:PDF格式的书籍可以方便地进行复制和搜索,使得我们可以快速找到我们需要的内容,并方便地进行引用和参考。 2. 纸质书籍的替代品:PDF版本的书籍不占用实体空间,而且可以通过电子设备随时携带,方便在任何时间、任何地点进行学习和阅读。 3. 交互性强:PDF格式的书籍还可以添加书签、注释和标记,方便读者进行个性化的标记和笔记,更好地帮助记忆和理解。 总之,《OpenGL超级宝典》PDF版本是一种便捷、高效的学习OpenGL图形学编程的工具,通过这本书,读者可以系统地学习OpenGL的基础知识和开发技巧,从而提高自己在图形学编程领域的能力。 ### 回答2: OpenGL蓝宝书是一本关于OpenGL编程的经典教材,适合初学者和有一定编程基础的开发者。本书全面介绍了OpenGL的基础知识和常用编程技巧,并提供了大量的代码示例和实践项目。 这本书的PDF版本提供了方便的电子阅读方式,读者可以随时随地学习和实践OpenGL编程。使用PDF格式的优点是可以根据需要进行搜索、标注、复制和打印,在学习过程中方便查阅和注释,提高学习效率。 《OpenGL蓝宝书》的内容涵盖了OpenGL的基础知识,包括顶点和片元着色器、图元绘制、纹理映射、新的OpenGL特性等。此外,该书还介绍了OpenGL的高级技术,如光照、阴影、透明度、几何着色器等,帮助读者掌握更复杂的图形渲染技术。 这本书对于学习OpenGL编程的人来说是一部非常有价值的参考资料。它通过清晰的逻辑结构和易于理解的语言,帮助读者理解和掌握OpenGL的核心概念和编程技巧。同时,书中提供的示例代码和实践项目可以帮助读者巩固所学知识,并进一步探索OpenGL的应用。 总的来说,《OpenGL蓝宝书》的PDF版本是一种方便快捷的学习OpenGL编程的方式,可以满足读者在不同场合和需求下的学习和实践需求。无论是初学者还是有一定经验的开发者,都可以从中获得宝贵的知识和经验。 ### 回答3: OpenGL蓝宝书是一本经典的OpenGL编程指南,全书详尽地介绍了OpenGL的基础知识和编程技巧。这本书的完整版可以在网上找到PDF格式的电子书。通过阅读OpenGL蓝宝书,我们可以了解到OpenGL的底层原理、渲染管线以及各种常用的绘图和渲染技术。 OpenGL是一种跨平台的图形编程接口,它可以用于开发2D和3D图形应用程序。蓝宝书从基础概念开始,逐步介绍OpenGL的各个方面,包括顶点缓冲对象、着色器、纹理映射、光照等。通过例子和代码实践,读者可以深入理解OpenGL的工作原理,并学会如何使用OpenGL进行图形渲染。 OpenGL蓝宝书的PDF版本提供了便捷的学习方式。电子版本便于阅读和搜索,可以随时随地进行学习。同时,通过电子书的书签和目录功能,读者可以方便地定位到自己感兴趣的章节和内容。此外,电子书的PDF格式可以在多个设备上使用,如电脑、平板电脑和手机等。 总之,OpenGL蓝宝书PDF是一本非常有价值的OpenGL学习资料,它为初学者提供了一个系统而又详细的学习路径,帮助读者深入理解OpenGL的基础知识和编程技巧。无论是对于想要学习图形编程的人来说,还是对于已经对OpenGL有一定了解的开发者来说,这本书都是一本非常值得阅读的指南。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值