opengl渲染器入门03

引言

本文主要对于一些缓冲,混合和测试进行分析和处理。

深度测试

深度测试是用来保证物体的先后顺序,深度值小的值会被渲染到前面,深度值比较大的值会被渲染到后面,所以深度测试对于3D渲染是非常重要的,在深度测试中,深度测试的值是属于[0 1]的,这个值越小,就会距离屏幕近,越大就会距离屏幕越远。

在opengl中,初始值是1,距离最远,这里是一些关于深度测试的常用的函数。

js 复制代码
glEnable(GL_DEPTH_TEST);     //开启深度测试
glClear( GL_DEPTH_BUFFER_BIT);    //清楚深度缓冲
glDepthMask(GL_FALSE);         //禁用深度写入
glDepthFunc(GL_LESS);     //深度测试函数,小于的时候通过

在计算的过程中,需要计算出每一个点的深度值,一般情况有线性插值和非线性插值:


在一般时候,是不会使用线性插值,是使用第二种插值。设在相机下的空间的坐标是(x,y,z,w),设在投影变化后的空间的坐标是(x',y',z',w').由于在变化的过程中,深度值不再是线性的关系,对Z进行了一个压缩,如果想要得到真实的深度,只能先通过屏幕的深度值进行一个"逆NDC变换"和一个逆投影变化。

先给出正投影的变化矩阵:


设在NDC空间下的空间坐标是(x_NDC,y_NDC,z_NDC,w_NDC),而屏幕空间下的深度为depth,则有:


通过这个可以反推:


这样z、z_NDC和depth之间的关系我们就捋清楚了,z就是真实线性深度,depth是屏幕空间的非线性深度,之间做个转换即可:

js 复制代码
#version 330 core
out vec4 FragColor;
float near = 0.1;
float far = 100.0;
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0;
return (2.0 * near * far) / (far + near - z * (far - near));
}
void main()
{
float depth = LinearizeDepth(gl_FragCoord.z) / far;
FragColor = vec4(vec3(depth), 1.0);
}

关于深度冲突一般有三种方法来进行处理,首先是不要把物体放置的过于的近,然后是近平面远一点,或者使用更高精度的缓冲。

模版测试

模板测试和深度测试还是很相似,一般被用来控制是否渲染,不同的是,模板的初始的值为0.

js 复制代码
glEnable(GL_STENCIL_TEST);     //开启模板测试
glClear(GL_STENCIL_BUFFER_BIT);   //清楚模板缓冲
glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样(正常写入)
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)
glStencilFunc(GL_EQUAL, 1, 0xFF)   //当前的模板值和oxff与运算,不等于1时通过模板测试
glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)  //模板失败 模板成功深度失败 都成功

一般这个可以被用来检测轮廓算法

js 复制代码
//轮廓算法
glEnable(GL_DEPTH_TEST);   //开启深度测试 初始值为1,为最远的距离
glDepthFunc(GL_LESS);      //小于深度值通过
glEnable(GL_STENCIL_TEST);   //开启模板测试
glStencilFunc(GL_NOTEQUAL, 1, 0xff);     //通过的条件
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);     //检测控制,当通过的时候模板值切换为1
while (!glfwWindowShouldClose(window))
{
    glClearColor(0.1f,0.1f,0.1f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);   //清楚颜色,深度,模板缓冲
    glStencilMask(0x00); //禁用模板写入
    shader.use();   //普通着色器
    glDrawArrays(GL_TRIANGLES, 0, 6);   //渲染其他物体(不需要轮廓)
    //准备渲染有轮廓的物体
    glStencilFunc(GL_ALWAYS, 1, 0xFF);  //设置模板函数,所有的值全部通过
    glStencilMask(0xFF);   //开启模板写入,写入模板值为1(ref)
    glDrawArrays(GL_TRIANGLES, 0, 36);  //渲染需要轮廓的物体,(可以切换该物体的着色器)
    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);  //更改模板函数,不等于的1模板值通过
    glStencilMask(0x00);      //禁用模板写入
    glDisable(GL_DEPTH_TEST);  //关闭深度着色器
    shaderSingleColor.use();   //轮廓着色器
    float scale = 1.1f;      //实现顶点外扩
    glDrawArrays(GL_TRIANGLES, 0, 36);   //渲染边界
    
    glStencilMask(0xFF);      //开启模板测试
    glStencilFunc(GL_ALWAYS, 0, 0xFF);  //设置模板函数
    glEnable(GL_DEPTH_TEST);     //关闭深度测试
}

主要是渲染了原来的大小,通过顶点外扩变为原来的1.1倍,多了0.1,占用到了模板值为0的区域,通过设置模板函数只渲染模板值为0的地方就实现了轮廓。

混合

混合和透明度息息相关,是用来实现透明度的一种方法,值为a,当a为0的时候,完全透明,为1的时候,完全不透明。

关于混合的相关的函数

js 复制代码
glEnable(GL_BLEND);  //开启混合
glBlendFunc(GLenum sfactor, GLenum dfactor); //混合函数
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); //设置不同的选项

关于半透明纹理的渲染,先绘制所有的不透明的物体,然后对于所有的透明的物体的进行排序,由远到近绘制所有的物体。

相关推荐
凌云行者18 小时前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
凌云行者18 小时前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
凌云行者3 天前
OpenGL入门004——使用EBO绘制矩形
c++·cmake·opengl
闲暇部落4 天前
Android OpenGL ES详解——模板Stencil
android·kotlin·opengl·模板测试·stencil·模板缓冲·物体轮廓
凌云行者6 天前
OpenGL入门003——使用Factory设计模式简化渲染流程
c++·cmake·opengl
凌云行者7 天前
OpenGL入门002——顶点着色器和片段着色器
c++·cmake·opengl
闲暇部落8 天前
Android OpenGL ES详解——裁剪Scissor
android·kotlin·opengl·窗口·裁剪·scissor·视口
彭祥.13 天前
点云标注工具开发记录(四)之点云根据类别展示与加速渲染
pyqt·opengl
闲暇部落16 天前
android openGL ES详解——缓冲区VBO/VAO/EBO/FBO
kotlin·opengl·缓冲区·fbo·vbo·vao·ebo
闲暇部落17 天前
android openGL ES详解——混合
android·opengl