----------------------------------------------
LearnOpenGL
----------------------------------------------
OpenGL基础知识:
https://www.opengl.org/:OpenGL官方网站。
https://www.opengl.org/registry/:包含OpenGL各版本的规范和扩展。
https://learnopengl-cn.github.io
https://khronos.org/registry/OpenGL/specs/gl/glspec33.core.pdf
http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf
版本
早期的OpenGL使用立即渲染模式(Immediate mode,也就是固定渲染管线),这个模式下绘制图形很方便。OpenGL的大多数功能都被库隐藏起来,开发者很少能控制OpenGL如何进行计算的自由。而开发者迫切希望能有更多的灵活性。随着时间推移,规范越来越灵活,开发者对绘图细节有了更多的掌控。立即渲染模式确实容易使用和理解,但是效率太低。因此从OpenGL3.2开始,规范文档开始废弃立即渲染模式,推出核心模式(Core-profile)
所有OpenGL的更高的版本都是在3.3的基础上,引入了额外的功能,并没有改动核心架构。
简写
VAO(Vertex Array Object) : 顶点数组对象:
VBO(Vertex Buffer Object) : 顶点缓冲对象
EBO(Element Buffer Object): 索引缓冲对象
IBO(Index Buffer Object):
TBO(Texture Buffer Object):Uniform数据容量是有限的。故可用TBO,把数据装入一个一维纹理的Buffer中以提供给Shader
UBO(Uniform buffer object): 可供Shader间共享Uniform 变量 UBO:http://blog.csdn.net/panda1234lee/article/details/71326063
layout(std140) uniform matVP
{
mat4 matProj;
mat4 matView;
};
OpenGL 坐标系
y(0,1,0)
|
.----x(1,0,0)
/
z(0,0,1)
标准化设备坐标(Normalized Device Coordinates)范围:[-1, 1]
----------------------------------------------
Shader
----------------------------------------------
渲染管道
VertexShader : 顶点着色器,输入一个顶点,输出一个顶点
ShapeAssmbly : 形状(图元)装配。所有顶点作为输入,并所有的点装配成指定图元的形状
GeometryShader : 几何着色器。把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状
Rasterization : 光栅化阶段。把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。并执行裁切(Clipping)
FragmentShader : 片段着色器。主要目的是计算一个像素的最终颜色
TestAndBlending : Alpha测试和混合阶段。判断这个像素是其它物体的前面还是后面,决定是否应该丢弃
着色器文件类型
.vert:顶点着色器(Vertex Shader)
.frag:片段着色器(Fragment Shader)
.geom:几何着色器(Geometry Shader)
.tesc:细分控制着色器(Tessellation Control Shader)
.tese:细分计算着色器(Tessellation Evaluation Shader)
.comp:计算着色器(Compute Shader)
Shader 数据类型(http://blog.csdn.net/peeno/article/details/52996589)
void 跟C语言的void类似,表示空类型。作为函数的返回类型,表示这个函数不返回值。
bool 布尔类型,可以是true 和false,以及可以产生布尔型的表达式。
int 整型 代表至少包含16位的有符号的整数。可以是十进制的,十六进制的,八进制的。
float 浮点型
bvec2 包含2个布尔成分的向量
bvec3 包含3个布尔成分的向量
bvec4 包含4个布尔成分的向量
ivec2 包含2个整型成分的向量
ivec3 包含3个整型成分的向量
ivec4 包含4个整型成分的向量
mat2 或者 mat2x2 2x2的浮点数矩阵类型
mat3或者mat3x3 3x3的浮点数矩阵类型
mat4x4 4x4的浮点矩阵
mat2x3 2列3行的浮点矩阵(OpenGL的矩阵是列主顺序的)
mat2x4 2列4行的浮点矩阵
mat3x2 3列2行的浮点矩阵
mat3x4 3列4行的浮点矩阵
mat4x2 4列2行的浮点矩阵
mat4x3 4列3行的浮点矩阵
sampler1D 用于内建的纹理函数中引用指定的1D纹理的句柄。只可以作为一致变量或者函数参数使用
sampler2D 二维纹理句柄
sampler3D 三维纹理句柄
samplerCube cube map纹理句柄
sampler1DShadow 一维深度纹理句柄
sampler2DShadow 二维深度纹理句柄
------------------------------
vec(x,y,z,w)
可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可,如
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw
内置变量
顶点着色器可用的内置变量
gl_Color vec4 输入属性-表示顶点的主颜色
gl_SecondaryColor vec4 输入属性-表示顶点的辅助颜色
gl_Normal vec3 输入属性-表示顶点的法线值
gl_Vertex vec4 输入属性-表示物体空间的顶点位置
gl_MultiTexCoordn vec4 输入属性-表示顶点的第n个纹理的坐标
gl_FogCoord float 输入属性-表示顶点的雾坐标
gl_Position vec4 输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操作。所有的顶点着色器都必须写这个值。
gl_ClipVertex vec4 输出坐标,用于用户裁剪平面的裁剪
gl_PointSize float 点的大小
gl_FrontColor vec4 正面的主颜色的varying输出
gl_BackColor vec4 背面主颜色的varying输出
gl_FrontSecondaryColor vec4 正面的辅助颜色的varying输出
gl_BackSecondaryColor vec4 背面的辅助颜色的varying输出
gl_TexCoord[] vec4 纹理坐标的数组varying输出
gl_FogFragCoord float 雾坐标的varying输出
片段着色器可用内置变量
gl_Color vec4 包含主颜色的插值只读输入
gl_SecondaryColor vec4 包含辅助颜色的插值只读输入
gl_TexCoord[] vec4 包含纹理坐标数组的插值只读输入
gl_FogFragCoord float 包含雾坐标的插值只读输入
gl_FragCoord vec4 只读输入,窗口的x,y,z和1/w
gl_FrontFacing bool 只读输入,如果是窗口正面图元的一部分,则这个值为true
gl_PointCoord vec2 点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的情况下。
gl_FragData[] vec4 使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
gl_FragColor vec4 输出的颜色用于随后的像素操作
gl_FragDepth float 输出的深度用于随后的像素操作,如果这个值没有被写,则使用固定功能管线的深度值代替
内建函数(https://www.khronos.org/registry/OpenGL-Refpages/gl4/)——
数学函数
三角函数
纹理函数
三维函数
基础Shader示例
VertexShader
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position.x, position.y, position.z, 1.0);
}
FragmengShader
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
GeometryShader
可以修改模型。可以对每个顶点附近的数据进行访问,然后使用这些数据或生成新的几何形体。
如对折线进行besizer插值,变为平滑曲线
如对三角形进行插值,变为一个曲面
这个功能原先是一些三维软件进行后期渲染时的技术,现在被加入显卡中,可用于实时渲染
缺点:增加了面片,会降低效率。
http://blog.csdn.net/panda1234lee/article/details/71248763
动态生成顶点需要三维知识,慢慢研究吧
参数和传递
变量在shader中传递(in out)
顶点着色器
layout (location = 0) in vec3 position; // position变量的属性位置值为0
out vec4 vertexColor; // 为片段着色器指定一个颜色输出
void main()
{
gl_Position = vec4(position, 1.0); // 注意我们如何把一个vec3作为vec4的构造器的参数
vertexColor = vec4(0.5f, 0.0f, 0.0f, 1.0f); // 把输出变量设置为暗红色
}
片段着色器
in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
out vec4 color; // 片段着色器输出的变量名可以任意命名,类型必须是vec4
void main()
{
color = vertexColor;
}
在GL3.x中,废弃了attribute关键字(以及varying关键字),属性变量统一用in/out作为前置关键字
Uniform变量
全局只读变量,由外部传递给shader,各个shader中共用
out vec4 color;
uniform vec4 ourColor; // 在OpenGL程序代码中设定这个变量
void main()
{
color = ourColor;
}
uniform mat4 viewProjMatrix; //投影+视图矩阵
uniform mat4 viewMatrix; //视图矩阵
uniform vec3 lightPosition; //光源位置
attribute变量
只能在vertex shader中使用的变量
一般用来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。
attribute vec4 a_position;
attribute vec2 a_texCoord0;
一般用函数glBindAttribLocation()来绑定每个attribute变量的位置,然后用函数glVertexAttribPointer()为每个attribute变量赋值。
所以一些奇怪的各平台都不一致的变量就是这么来的
varying变量
是vertex和fragment shader之间做数据传递用的。
一般vertex shader修改varying变量的值,然后fragment shader使用该varying变量的值。
常用的全局变量
gl_Position
gl_FragColor
gl_VertexID
线段模式/面片填充模式?
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL|GL_LINE);
----------------------------------------------
纹理 Texture
----------------------------------------------
纹理坐标系
y(0,1)
|
.----x
(0,0) (1,0)
范围:[0, 1]
纹理坐标看起来就像这样:
GLfloat texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};
多级渐远纹理(Mipmap)
距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个
应用纹理示例(片段着色器)
in vec3 ourColor;
in vec2 TexCoord;
out vec4 color;
uniform sampler2D ourTexture;
void main()
{
color = texture(ourTexture, TexCoord); // 用纹理
color = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0f); // 用纹理和色彩混合
color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2); // 两种纹理混合
}
纹理颠倒问题
OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部
TexCoord = vec2(texCoord.x, 1.0f - texCoord.y);
----------------------------------------------
向量/矩阵/变换/坐标系
https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/
----------------------------------------------
向量(就是n*1矩阵)
v = (x, y) : 一般用于表示方向,原点在(0,0)
v+w = (v.x+w.x, v.y+w.y) : 向量相加
v-w = (v.x-w.x, v.y-w.y) : 向量相减
|v| = sqrt(x*2+y*2) : 向量长度
n=v/|v| : 标准化向量。它的长度是1
v¯⋅k¯=||v¯||⋅||k¯||⋅cosθ : 向量点乘,如(0,6, -0.8)⋅(0, 1) = (0.6*0)+(-0.8*1)=-0.8 --> 143.1度
v¯⋅k¯=1⋅1⋅cosθ=cosθ : 标准化向量点乘,可用于得到夹角。
v*k : 叉乘只在3D空间中有定义,它需要两个不平行向量作为输入,生成一个正交于两个输入向量的第三个向量
单位矩阵(IdentityMatrix)
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
乘以一个向量完全不变
放缩
Sx 0 0 0
0 Sy 0 0
0 0 Sz 0
0 0 0 1
位移
1 0 0 Tx
0 1 0 Ty
0 0 1 Tz
0 0 0 1
旋转(沿z轴旋转)
cosθ sinθ 0 0
−sinθ cosθ 0 0
0 0 1 0
0 0 0 1
任意角度旋转(http://v.youku.com/v_show/id_XMTgxNDgzMjUyNA==.html?spm=a2h0j.8191423.module_basic_relation.5~5!2~5~5!3~5!2~1~3~A)
欧拉角(Euler Angles):
偏航角(Yaw):Ry
俯仰角(Pitch):Rx
滚转角(Roll):Rz
坐标系转换
https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/
空间和坐标系
局部空间(Local Space): 一个物体的初始空间。所有的坐标都是相对于物体的原点的。
世界空间(World Space): 所有的坐标都相对于全局原点。
观察空间(View Space): 所有的坐标都是从摄像机的视角观察的。
裁剪空间(Clip Space): 所有的坐标都是从摄像机视角观察的,但是该空间应用了投影。这个空间应该是一个顶点坐标最终的空间,作为顶点着色器的输出。OpenGL负责处理剩下的事情(裁剪/透视除法)。
屏幕空间(Screen Space): 所有的坐标都由屏幕视角来观察。坐标的范围是从0到屏幕的宽/高。
电脑上看到的
剪裁矩阵 = 投影矩阵 视图矩阵 模型矩阵 本地矩阵
Vclip = Mprojection ⋅ Mview ⋅ Mmodel ⋅ Vlocal
-------------------------------------------
顶点着色器
#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f); // 矩阵是右边到左边乘
}
相机及LookAt矩阵
摄像机位置
目标位置
世界空间中的上向量
----------------------------------------------
色彩,材质,贴图
----------------------------------------------
物体的色彩=光线照到物体表面后,反射的光进入眼睛的色彩,其余的光被物体吸收了。
物体色彩=光照色彩*反射色彩
glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);
片段着色器
out vec4 color;
uniform vec3 objectColor;
uniform vec3 lightColor;
void main()
{
color = vec4(lightColor * objectColor, 1.0f);
}
冯氏光照模型材质(Phong Lighting Model)。
https://learnopengl-cn.github.io/02%20Lighting/02%20Basic%20Lighting/
冯氏光照模型的主要结构由3个元素组成:
环境(Ambient):无论如何永远都给物体一些颜色
漫反射(Diffuse):模拟一个发光物对物体的方向性影响,面向光源的一面比其他面会更亮
镜面高光(Specular):模拟有光泽物体上面出现的亮点。镜面光照的颜色,相比于物体的颜色更倾向于光的颜色。
Phong 材质的基本结构
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
常用材质(http://devernay.free.fr/cours/opengl/materials.html)
计算示例
void main()
{
// 环境光
vec3 ambient = lightColor * material.ambient;
// 漫反射光
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// 镜面高光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = lightColor * (spec * material.specular);
// 最后色彩
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
}
漫反射贴图材质(Diffuse texture)
struct Material
{
sampler2D diffuse;
vec3 specular;
float shininess;
};
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
漫反射高光贴图材质
struct Material
{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
color = vec4(ambient + diffuse + specular, 1.0f);
立方体贴图(CubeMap)
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/06%20Cubemaps/
立方体贴图它包含6个2D纹理
面片着色器
in vec3 textureDir; // 用一个三维方向向量来表示立方体贴图纹理的坐标
uniform samplerCube cubemap; // 立方体贴图纹理采样器
void main()
{
color = texture(cubemap, textureDir);
}
天空盒
天空盒常用立方体+加立方体贴图模拟。为什么不用球体?那球要很大很大很大才行
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/06%20Cubemaps/
http://www.custommapmakers.org/skyboxes.php
反射(可用于做金属效果)
将环境贴图融合到物体表面
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/06%20Cubemaps/
vertexShader
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
out vec3 Normal;
out vec3 Position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
Normal = mat3(transpose(inverse(model))) * normal;
Position = vec3(model * vec4(position, 1.0f));
}
fragmentShader
#version 330 core
in vec3 Normal;
in vec3 Position;
out vec4 color;
uniform vec3 cameraPos;
uniform samplerCube skybox;
void main()
{
vec3 I = normalize(Position - cameraPos);
vec3 R = reflect(I, normalize(Normal));
color = texture(skybox, R);
}
折射(可用于玻璃制物体)
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/06%20Cubemaps/
void main()
{
float ratio = 1.00 / 1.52;
vec3 I = normalize(Position - cameraPos);
vec3 R = refract(I, normalize(Normal), ratio);
color = texture(skybox, R);
}
双面纹理
#version 330 core
out vec4 color;
in vec2 TexCoords;
uniform sampler2D frontTexture;
uniform sampler2D backTexture;
void main()
{
if(gl_FrontFacing)
color = texture(frontTexture, TexCoords);
else
color = texture(backTexture, TexCoords);
}
法线凹凸贴图(NormalMapping)
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/
每个fragment使用了自己的法线
法线来源一个法线贴图图像文件(大部分为蓝绿色)
法线数据 rgba->xyzw, 故物体正面偏蓝,顶部偏绿,右侧偏红
视差贴图(ParallaxMapping)
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/05%20Parallax%20Mapping/
每个顶点都根据从高度贴图采样出来的高度值进行位移,根据材质的几何属性平坦的平面变换成凹凸不平的表面
视差贴图尝试模拟深度,能够根据你观察它们的方向使砖块叠加到其他砖块上。
----------------------------------------------
光源
请和材质结合在一起看
----------------------------------------------
点光源(向所有方向发射光线,如灯泡)
定义
struct PointLight {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
照到物体上后显示的色彩
vec3 ambient = light.ambient * material.ambient;
vec3 diffuse = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);
// 计算定点光在确定位置的光照颜色
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - fragPos);
// 计算漫反射强度
float diff = max(dot(normal, lightDir), 0.0);
// 计算镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// 计算衰减
float distance = length(light.position - fragPos);
float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
// 将各个分量合并
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
return (ambient + diffuse + specular);
}
方向光(如很远的太阳光)
方向光
struct DirectionLight
{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
// 二次衰减
float constant;
float linear;
float quadratic;
};
float distance = length(light.position - FragPos);
float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
// 计算漫反射强度
float diff = max(dot(normal, lightDir), 0.0);
// 计算镜面反射强度
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// 合并各个光照分量
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
return (ambient + diffuse + specular);
}
聚光(如舞台聚光灯)
https://learnopengl-cn.github.io/02%20Lighting/05%20Light%20casters/
定义
struct SpotLight
{
vec3 position;
vec3 direction;
float cutOff;
...
};
柔化边缘
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
light.diffuse* = intensity;
specular* = intensity;
环境光
统一加个参数,增加所有亮度
更逼真的方案是:环境光遮蔽
环境光遮蔽(Ambient Occlusion)
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/09%20SSAO/
它的原理是通过将褶皱、孔洞和非常靠近的墙面变暗的方法近似模拟出间接光照,这些区域很大程度上是被周围的几何体遮蔽的,光线会很难流失,所以这些地方看起来会更暗一些。
可以模拟出阴影效果,让场景更加逼真
在2007年,Crytek公司发布了一款叫做屏幕空间环境光遮蔽(Screen-Space Ambient Occlusion, SSAO)的技术,并用在了他们的看家作孤岛危机上。这一技术使用了屏幕空间场景的深度而不是真实的几何体数据来确定遮蔽量。
----------------------------------------------
高级议题
----------------------------------------------
深度测试
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/01%20Depth%20testing/
模版测试
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/02%20Stencil%20testing/
半透明和混合
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/03%20Blending/
面剔除(Face culling)
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/04%20Face%20culling/
OpenGL允许检查所有正面朝向(Front facing)观察者的面,并渲染它们,而丢弃所有背面朝向(Back facing)的面。以增加性能。
glEnable(GL_CULL_FACE); // 所有的不是正面朝向的面都会被丢弃。
glCullFace(GL_BACK); // 也可以手工指定抛弃的面. 只剔除背面
帧缓冲 FBO
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/05%20Framebuffers/
实例化
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/10%20Instancing/
共用模型,增加性能的方法。常用于渲染大量的同模型的物体,如陨石带。
实例渲染通常用来渲染草、草丛、粒子以及像这样的场景
抗锯齿
Anti Aliasing
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/11%20Anti%20Aliasing/
阴影贴图(shadow mapping)
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/03%20Shadows/01%20Shadow%20Mapping/
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/03%20Shadows/02%20Point%20Shadows/
深度测试+柔化+。。。
HDR(高动态范围 (High Dynamic Range)
如多个光源叠加,色彩值会大于1.0,传统做法是限制在1.0,但这样会丢失细节
HDR 高动态范围的思路是,局部色彩值大于1.0,全屏依据比例调整色彩值到0-1.0间。以保留细节
允许用更大范围的颜色值渲染从而获取大范围的黑暗与明亮的场景细节,最后将所有HDR值转换成在[0.0, 1.0]范围的LDR(Low Dynamic Range,低动态范围)。转换HDR值到LDR值得过程叫做色调映射(Tone Mapping)
光晕,泛光(bloom)
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/07%20Bloom/
像平时那样渲染一个有光场景,提取出场景的HDR颜色缓冲以及只有这个场景明亮区域可见的图片。被提取的带有亮度的图片接着被模糊,结果被添加到HDR场景上面
延迟着色(Deferred Shading or Rendering)
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/08%20Deferred%20Shading/
正向渲染(Forward Rendering): 在场景中我们根据所有光源照亮一个物体,之后再渲染下一个物体,以此类推
延迟渲染(Deferred Rendering): 优化拥有大量光源的场景。
原理看不懂。。。。反正用于适合渲染多光源到就是了。
缺点:大内存开销,没有MSAA和混合(仍需要正向渲染的配合)。
基于物理的渲染(PBR Physically Based Rendering)
https://learnopengl-cn.github.io/07%20PBR/01%20Theory/
就是非常逼真的模拟现实
满足以下三个条件
基于微平面(Microfacet)的表面模型。
能量守恒:出射光线的能量永远不能超过入射光线的能量(发光面除外)
应用基于物理的BRDF。
一个逼真的金属球体可能用到以下材质
albedo : 反射率。模拟锈
normal : 凹凸法线
metallic : 金属质地
roughness : 粗糙质地
ao : 环境光遮蔽贴图
----------------------------------------------
后期处理
----------------------------------------------
反色
void main()
{
color = vec4(vec3(1.0 - texture(screenTexture, TexCoords)), 1.0);
}
灰度
void main()
{
color = texture(screenTexture, TexCoords);
float average = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
color = vec4(average, average, average, 1.0);
}
锐化
const float offset = 1.0 / 300;
void main()
{
vec2 offsets[9] = vec2[](
vec2(-offset, offset), // top-left
vec2(0.0f, offset), // top-center
vec2(offset, offset), // top-right
vec2(-offset, 0.0f), // center-left
vec2(0.0f, 0.0f), // center-center
vec2(offset, 0.0f), // center-right
vec2(-offset, -offset), // bottom-left
vec2(0.0f, -offset), // bottom-center
vec2(offset, -offset) // bottom-right
);
float kernel[9] = float[](
-1, -1, -1,
-1, 9, -1,
-1, -1, -1
);
vec3 sampleTex[9];
for(int i = 0; i < 9; i++)
{
sampleTex[i] = vec3(texture(screenTexture, TexCoords.st + offsets[i]));
}
vec3 col = vec3(0.0);
for(int i = 0; i < 9; i++)
col += sampleTex[i] * kernel[i];
color = vec4(col, 1.0);
}
模糊
float kernel[9] = float[](
1.0 / 16, 2.0 / 16, 1.0 / 16,
2.0 / 16, 4.0 / 16, 2.0 / 16,
1.0 / 16, 2.0 / 16, 1.0 / 16
);
边缘检测
float kernel[9] = float[](
1, 1, 1,
1, -8, 1,
1, 1, 1
);
----------------------------------------------
几何着色器(更改原有模型,如实现曲面效果/显示法线)
----------------------------------------------
几何着色器
示例(
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;
void build_house(vec4 position)
{
gl_Position = position + vec4(-0.2f, -0.2f, 0.0f, 0.0f);// 1:左下角
EmitVertex();
gl_Position = position + vec4( 0.2f, -0.2f, 0.0f, 0.0f);// 2:右下角
EmitVertex();
gl_Position = position + vec4(-0.2f, 0.2f, 0.0f, 0.0f);// 3:左上
EmitVertex();
gl_Position = position + vec4( 0.2f, 0.2f, 0.0f, 0.0f);// 4:右上
EmitVertex();
gl_Position = position + vec4( 0.0f, 0.4f, 0.0f, 0.0f);// 5:屋顶
EmitVertex();
EndPrimitive();
}
void main()
{
build_house(gl_in[0].gl_Position);
}
爆破物体
把每个三角形沿着它们的法线向量移动一小段距离
geometryShader
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/09%20Geometry%20Shader/
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
in VS_OUT {
vec2 texCoords;
} gs_in[];
out vec2 TexCoords;
uniform float time;
vec4 explode(vec4 position, vec3 normal)
{
float magnitude = 2.0f;
vec3 direction = normal * ((sin(time) + 1.0f) / 2.0f) * magnitude;
return position + vec4(direction, 0.0f);
}
vec3 GetNormal()
{
vec3 a = vec3(gl_in[0].gl_Position) - vec3(gl_in[1].gl_Position);
vec3 b = vec3(gl_in[2].gl_Position) - vec3(gl_in[1].gl_Position);
return normalize(cross(a, b));
}
void main()
{
vec3 normal = GetNormal();
gl_Position = explode(gl_in[0].gl_Position, normal);
TexCoords = gs_in[0].texCoords;
EmitVertex();
gl_Position = explode(gl_in[1].gl_Position, normal);
TexCoords = gs_in[1].texCoords;
EmitVertex();
gl_Position = explode(gl_in[2].gl_Position, normal);
TexCoords = gs_in[2].texCoords;
EmitVertex();
EndPrimitive();
}
显示每个面的法线
vertextShader
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
out VS_OUT {
vec3 normal;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
mat3 normalMatrix = mat3(transpose(inverse(view * model)));
vs_out.normal = normalize(vec3(projection * vec4(normalMatrix * normal, 1.0)));
}
geometryShader
#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices = 6) out;
in VS_OUT {
vec3 normal;
} gs_in[];
const float MAGNITUDE = 0.4f;
void GenerateLine(int index)
{
gl_Position = gl_in[index].gl_Position;
EmitVertex();
gl_Position = gl_in[index].gl_Position + vec4(gs_in[index].normal, 0.0f) * MAGNITUDE;
EmitVertex();
EndPrimitive();
}
void main()
{
GenerateLine(0); // First vertex normal
GenerateLine(1); // Second vertex normal
GenerateLine(2); // Third vertex normal
}
----------------------------------------------
调试工具
----------------------------------------------
https://learnopengl-cn.github.io/06%20In%20Practice/01%20Debugging/