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

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

相关推荐
byxdaz10 天前
Qt OpenGL 3D 编程入门
qt·opengl
byxdaz11 天前
Qt OpenGL 相机实现
opengl
二进制人工智能1 个月前
【OpenGL学习】(二)OpenGL渲染简单图形
c++·opengl
六bring个六1 个月前
qtcreater配置opencv
c++·qt·opencv·计算机视觉·图形渲染·opengl
爱看书的小沐1 个月前
【小沐学GIS】基于C++绘制二维瓦片地图2D Map(QT、OpenGL、GIS)
c++·qt·gis·opengl·glfw·glut·二维地图
六bring个六1 个月前
图形渲染+事件处理最终版
c++·qt·图形渲染·opengl
星火撩猿1 个月前
OpenGl实战笔记(3)基于qt5.15.2+mingw64+opengl实现光照变化效果
笔记·qt·opengl·光照效果
星火撩猿1 个月前
OpenGl实战笔记(2)基于qt5.15.2+mingw64+opengl实现纹理贴图
笔记·qt·opengl·纹理贴图
程序员爱德华1 个月前
计算机图形学中的深度学习
图形学·opengl
:mnong1 个月前
开放原子大赛石油软件赛道参赛经验分享
c++·qt·hdfs·开放原子·图形渲染·webgl·opengl