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); //设置不同的选项

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

相关推荐
扶尔魔ocy4 天前
【qt openGL入门笔记】windows编译MinGW版本的osgearth(2.10.1)
qt·opengl·osg
扶尔魔ocy5 天前
【QT OpenGL】知识点编目及传送门
qt·opengl·osg
扶尔魔ocy18 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行并进行态势标绘(四)
qt·opengl·osg
:mnong19 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy20 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy20 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy21 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy25 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy25 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy1 个月前
【qt openGL入门笔记】基础光照
qt·opengl