Early-Z and Pre-Z -- 介绍与区别
Depth Testing 深度测试在介绍Early-Z和Pre-Z之前,我们首先得了解深度测试.
深度测试是用于解决物体可见性问题(是否被遮挡)的技术.在渲染管线中位于片元着色器之后
但是深度测试会带来一个问题:
即有的片元在经历了着色后,在深度测试环节被直接抛弃了,这显然是对算力的浪费–我们完全可以不用计算这些片元.我们把这一问题称为OverDraw问题.
Early-Z就是这个问题的一种解决方案.
Early-ZEarly-Z是在光栅化之后,片元着色器之前增加的一步操作
在这一流程中,会进行一次深度测试,剔除掉所有不通过的片元,这样就减少了算力 的浪费.
读者可能会想,如果提前进行了深度测试,那最后不就不需要再进行深度测试了吗?
事实上,由于渲染管线的复杂性,我们需要再最后再次进行深度测试(用于区分可以叫做z-check)以支持透明物体,抗锯齿等等操作.
Pre-ZEarly-Z也存在一些问题:
物体由远及近渲染时,Early-Z效率较低.为提高效率需要CPU排序
不支持透明物体
Pre-Z可以解决这些问题.方法是,使用两个pass渲染:第一个pass不输出任何颜色,只写入深 ...
启发式算法
Intro首先考虑一个经典问题:旅行商问题(Traveling Salesman Problem, TSP).
或许你的第一想法式dijastra算法.但问题在于dijastra解决的是点到点的最短路径,而旅行商要转一圈回到原点,这是dijastra算法无法解决的问题.
或许你还会想到贪心算法.事实上,贪心算法正是启发式算法的一个子类.但是在这个问题中,什么贪心策略都无法保证全局最优解.
为什么我能这么肯定呢?因为旅行商问题是一个$NP难(NP-hard)$问题,要想得到最优解,就必须把所有可能枚举出来然后再做比较.
如果你能做到不枚举所有可能就得到最优解,那么今年的菲尔兹奖,图灵奖等等等等你肯定是要拿到手软了.
我们继续分析旅行商问题.如果有$n$个城市,则路径有$(n-1)!$种可能,显然当n变大时,计算量就要爆炸了.
当我们无法接受计算量带来的成本时,我们就需要找到一种虽然成本较低,但最终结果仍然不错的算法,而这就是$启发式算法$
启发式算法启发式算法是基于直观或经验构造的算法,在可接受的计算时间和空间条件下,给出待解决优化问题的一个可行解.
启发式算法并不保证找到最优解, ...
STL的神奇用法
元素去重set示例:
vector<int> nums = {1, 2, 3, 2, 1, 4, 5, 4, 6};set<int> s(nums.begin(), nums.end());nums.assign(s.begin(), s.end());
原理:
set(集合)–每个元素只出现一次,且元素有序(默认升序).
将vector中的元素赋值给set,得到一个元素唯一的集合.
将set中的元素赋值给vector,得到一个元素唯一且有序的vector.
消耗:
时间复杂度: O(n log n)
空间复杂度: O(n)
二分查找lower_bound()示例:
vector<int> nums = {1, 3, 5, 7, 9};int pos = lower_bound(nums.begin(), nums.end(), 5) - nums.begin();cout << "The position of 5 is " << pos << ...
OpenGL学习笔记(八)-光照
光照在上一节我们实现了材质.
材质用于表征一个表面的各种属性.
但是相同的材质在不同光照的情况下显现出的结果不同.例如:一件白衣服在不同灯光下显现的颜色不同.
所以最终像素点上的颜色应该由光照和材质共同决定.
光照模型有很多种,常见的有Phong光照模型,Blinn-Phong光照模型.我们在这一节实现一个Blinn-Phong光照模型.
Light类首先我们定义一个Light类,用于描述光源.
class Light{public: glm::vec3 position; glm::vec3 color; float radiant;public: Light(){ position = glm::vec3(0.0f, 0.0f, 0.0f); color = glm::vec3(1.0f, 1.0f, 1.0f); radiant = 10.0f; } Light(glm::vec3 pos, glm::vec3 col, float rad) { p ...
OpenGL学习笔记(七)-3d模型
Assimp库3d资产的格式种类非常之多,文件的细节与标准也千差万别,我们需要一个专门的库来为我们处理这些资产文件.Assimp库就是这样一个库,它可以读取各种3d资产文件,以便我们加载3d模型.
你可以点击这里访问Assimp库的GitHub页面,下载最新版本的Assimp库.
由于Assimp库实际上较为庞大,考虑到编译Assimp库需要的时间较长,我们选用Assimp库的预编译版本.你可以点击这里下载
我们将库安装在我们的3rd目录下.然后更改我们的CMakeLists.txt文件,添加以下内容:
include_directories(${CMAKE_SOURCE_DIR}/3rd/assimp/include)link_libraries(${CMAKE_SOURCE_DIR}/3rd/assimp/lib/x64)
别忘了在Source中的CMakeLists.txt中链接Assimp库:
target_link_libraries(BickRenderer ${CMAKE_SOURCE_DIR}/3rd/ass ...
OpenGL学习笔记(六)-坐标系统
概述本节核心就是一张图:
局部空间(Local Space)局部空间是物体所在的空间,比如在DCC软件中创建的模型所在的空间.
世界空间(World Space)世界空间是全局坐标系,为世界中的所有物体提供一个坐标系.
观察空间(View Space)观察空间是摄像机所在的空间,它是相机的视角,是摄像机看到的世界的局部空间.
裁剪空间(Clip Space)在我们的着色器中,我们希望坐标都能落在一个特定的范围,否则在着色器运算中会增加很多复杂的步骤,因此需要对观察空间进行裁剪变换到NDC空间.
应用glm库已经为我们实现好了计算这些变换矩阵的方法:
glm::mat4 model = glm::mat4(1.0f); // 模型矩阵glm::mat4 view = glm::mat4(1.0f); // 观察矩阵glm::mat4 projection = glm::mat4(1.0f); // 投影矩阵model = glm::translate(model, glm::vec3(0.0f, 0.0f, -3.0f)); // 平移view = glm::lookAt(glm: ...
OpenGL学习笔记(五)--3D数学
向量向量相乘
点乘:
叉乘:
注意,叉乘的方向取决于坐标系是左手系还是右手系.
常见软件使用坐标系如下:
OpenGL : 右手系
DirectX: 左手系
Unreal : 左手系
Unity : 左手系
glm : 右手系
矩阵矩阵乘法
值得注意的是,在矩阵乘法中,乘法交换律是不满足的.又由于各种平台对一维向量的表示不同(行向量还是列向量),所以在矩阵乘法中顺序尤为重要.
比如MVP矩阵,在OpenGL中使用的是列向量,所以MVP矩阵的乘法顺序是P * V * M * v.而在DirectX中使用的是行向量,所以MVP矩阵的乘法顺序是v * M * v * P .
以下是常见软件向量表示方法:
OpenGL : 列向量
Unity : 列向量
DirectX: 行向量
glm : 列向量
矩阵与向量相乘
我们的模型顶点一般是一个三维向量,我们可以利用举证对这个向量进行变换,比如平移,旋转,缩放等操作.
缩放矩阵
注意,第四个缩放向量仍然是1,因为在3D空间中缩放w分量是无意义的.w分量另有其他用途,在后文中会说明.
位移 ...
OpenGL学习笔记(四)--纹理
本节目录
纹理基础
纹理技术
应用纹理
纹理类
纹理基础图片实际上就是一张纹理,为了表现丰富的计算机世界,纹理是必不可少的.
首先需要解决的是如何将一张图片映射到我们的计算机世界中.我们使用x和y轴的两个0-1的数来表示纹理的坐标,即uv坐标
对于我们的三角形,我们可以为每个顶点指定uv坐标,这样就可以和纹理对应起来了.
纹理技术Wrapping当纹理z轴坐标超过1时,我们需要对纹理坐标进行wrap,也就是如何处理uv大于1的情况.OpenGL提供了四种选择:
GL_REPEAT: 重复纹理
GL_MIRRORED_REPEAT: 镜像重复纹理
GL_CLAMP_TO_EDGE: 贴边纹理
GL_CLAMP_TO_BORDER: 边缘纹理
效果如下:
MipMap想象一下,当镜头拉远,一个很远的物体只占屏幕的4个像素时,如果还让其在一张512x512的纹理上采样,想想都不太高效.所以可以生成各种分辨率的纹理,然后根据距离摄像机的远近选择合适的纹理.
这种方法带来的开销也非常小.
纹理过滤纹理是有分辨率的,但uv坐标”没有”.这就导致opengl需要知道如何将纹理像素映射到uv ...
OpenGL学习笔记(三)--Shader
本节内容
Shader基础
Shader类
什么是ShaderShader是运行在GPU上的程序.编写Shader程序使用的是Shading Language.主流的Shading Language有GLSL,HLSL,CG等.其中OpenGL使用的是GLSL.
Shader基本结构一个典型的shader有以下结构
#version version_numberin type in_variable_name;in type in_variable_name;out type out_variable_name;uniform type uniform_name;void main(){ // 处理输入并进行一些图形操作 ... // 输出处理过的结果到输出变量 out_variable_name = weird_stuff_we_processed;}
Shader类对于创建和使用一个shader程序,我们有以下几个固定的步骤.
unsigned int vertexShader, fragmentShader;const char *vertexSh ...
OpenGl学习笔记(二)--Triangle
本节内容
第一个三角形
Hello Triangle在本章之前首先需要明确以下几个名词
VAO(Vertex Array Object): 顶点数组对象,用于存储顶点数据
VBO(Vertex Buffer Object): 顶点缓冲对象,用于存储顶点数据
EBO(Element Buffer Object): 元素缓冲对象,用于存储索引数据
IBO(Index Buffer Object): 索引缓冲对象,用于存储索引数据
OpenGL 渲染管线
顶点着色器: 将顶点转换到NDC(Normalized Device Coordinates)
几何着色器: 处理顶点的几何变换
图元装配: 将顶点组成图元
光栅化: 将图元转换为像素
片段着色器: 计算每个像素的颜色
测试混合: 融合颜色
输入顶点数据由于我们想要渲染一个三角形,所以首先需要指定三个顶点.
float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f};
这里将顶点位置已经 ...