无意中看到一篇文章 “Real Shading in Unreal Engine 4”,还是 13 年的。要实现一个好的光照模型究竟有多难。 2013SiggraphPresentationsNotes-26915738.pdf from
vec3 lightDir = normalize(lightPos - FragPos);
vec3 viewDir = normalize(viewPos - FragPos);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), shininess);
vec3 specular = lightColor * spec;
void main()
{
[...]
float spec = 0.0;
if (blinn)
{
vec3 halfwayDir = normalize(lightDir + viewDir);
spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0);
}
else
{
vec3 reflectDir = reflect(-lightDir, normal);
spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0);
}
}
Gamma 校正是一个 上拱 的过程。
通过移动视口到光源位置,可以观察到这个位置每个东西都是明亮的,因为从光的角度来看是没有阴影的。 从光源的角度将场景的深度渲染到一张深度缓冲区中,我们可以在场景中获得一张阴影或者无阴影的贴图,一张阴影贴图。
高级光照阴影 ShadowMapping,实在没看透彻,源码抄了一遍,才透彻了。能感受到暴力美学。 高级光照阴影 ShadowMapping /blog/2020/12/24/shader-OpenGL-Lighting-Advanced-shadowMapping
伴随矩阵 (Adjugate Matrix):
\[\begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} = \frac{1}{\Delta U_1 \Delta V_2 - \Delta U_2 \Delta V_1} \begin{bmatrix} \Delta V_2 & -\Delta V_1 \\ -\Delta U_2 & \Delta U_1 \end{bmatrix} \begin{bmatrix} E_{1x} & E_{1y} & E_{1z} \\ E_{2x} & E_{2y} & E_{2z} \end{bmatrix}\]目前我们看到最终画面都是 2D 的,只能看到有限的像素数,理论上我们只要处理(指光照,阴影处理)最终我们可以看到的点的效果就够了, 多余的处理是浪费的。而正常的前向渲染 (Forward Shading) 流程是把空间的点进行各种剪裁后,进行处理,所处理量远远大于我们最终看到的。 所以延迟渲染出现了。它先将摄像机空间的点光栅化转化成屏幕坐标后再进行处理。这样就能减少处理的次数,从而提高效率。
我一直认为,延迟渲染跟阴影,是图形学的一个分水岭。能深刻的理解了延迟渲染、阴影,那么至少图形学算是入门了,至少对渲染架构,渲染的一些基本算法,入门了,不再是门外汉了。延迟渲染
延时其实指的是在渲染场景之后,再来进行光照计算,也就是延时光照计算在后期进行, 正向渲染的着色器采取物体 + 光照同时进行计算,也就是在渲染物体的时候,直接利用物体的物质,法线和颜色进行光照计算; 而延时渲染,是先渲染场景,在 G-buffer 中存储光照计算所需要的数据。from
/**
延迟渲染主要步骤:
1. 几何处理阶段;
2. 光照处理阶段;
*/
int main() {
// G-buffer 的创建
unsigned int gBuffer;
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
unsigned int gPosition, gNormal, gAlbedoSpec;
// position - 位置信息
// normal - 法线信息
// albedospec - 颜色信息
// 深度附件
// MRT-Multiple Render Targets 多渲染目标技术
unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1,GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);
// 多光源
// 渲染步骤
while (!glfwWindowShouldClose(window)) {
// 几何处理阶段
// 光照处理阶段
renderQuad();
if (LightBox)
{
// 绘制光源
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
// 绘制铺满整个屏幕的矩形
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad() {
}
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec3 FragPos;
out vec2 TexCoords;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
vec4 worldPos = model * vec4(aPos, 1.0);
FragPos = worldPos.xyz;
TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
Normal = normalMatrix * aNormal;
gl_Position = projection * view * worldPos;
}
#version 330 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;
uniform sampler2D texture_diffuse1;
void main()
{
gPosition = FragPos;
gNormal = normalize(Normal);
gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb;
}
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
uniform float num;
struct Light {
vec3 Position;
vec3 Color;
};
const int NR_LIGHTS = 50;
uniform Light lights[NR_LIGHTS];
uniform vec3 viewPos;
void main()
{
// 从 G-buffer 中提取数据
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = 1.0f;
// 光照计算
vec3 lighting = Diffuse * 0.9;
vec3 viewDir = normalize(viewPos - FragPos);
for(int i = 0; i < NR_LIGHTS; ++i)
{
// 环境光
vec3 ambient = Diffuse * 0.01;
// 漫反射
vec3 lightDir = normalize(lights[i].Position - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * lights[i].Color;
// 镜面高光
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0);
vec3 specular = lights[i].Color * spec * Specular;
lighting += ambient + diffuse + specular;
}
FragColor = vec4(lighting/num, 1.0);
}
屏幕空间环境光遮蔽 (Screen-Space Ambient Occlusion, SSAO)。 在 2007 年,Crytek 公司发布了一款叫做屏幕空间环境光遮蔽 (Screen-Space Ambient Occlusion, SSAO) 的技术,并用在了他们的看家作孤岛危机上。
Metaball。纯 Shader 实现,代码只有 200 多行,满满的数学知识。
Bounded metaballs with quintic falloff. Literature often recommends cubic (smoothstep) falloffs (rather than exponential or 1/d²) for they are bounded. However they produce discontinuities in normals/lighting. Quintic falloffs solves the problem.
更多高级光照知识:RTR4 拾遗(一)– 图形学的 B 面 PBR–RTR4 笔记 – 第八章 光与颜色