本篇在讲什么 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) //(模板)
帧缓存对象作为一个整体也必须是完整的,全帧缓存不完整的常见情形如下
当用户认为已经完成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_RGBA16E
和GL_RGBA32F
,如下所示
glTextureStorage2D(texture,1,GL_RGBA16F,width, height);
glTextureStorage2D(texture,1,GL_RGBA32F,width, height);
有许多可用格式如下表所示
格式 | 内容 |
---|---|
GL_RGBA32F | 4个32位浮点分量 |
GL_RGBA16F | 4个16位浮点分量 |
GL_RGB32F | 3 个32 位浮点分量 |
GL_RGB16F | 3个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,1024,1024);
如果输入电压达到最大电压的-半,则光输出还略低于最大可能光输出的四分之一!为了弥补这一点,在计算机图形中,使用伽马校正(以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);
https://github.com/KingSun5
若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。