Pixel World

it's better be burning out than to fade away.

Shadow Mapping

Basis

01.png

  • 第一个pass从光源位置和方向构造相机的viewProjectionMatrix,渲染深度图
  • 第二个pass将采样点转换到光照空间,比较深度判定遮挡性, 以下为shadowMap第二个pass计算阴影的关键代码:

VertexShader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#version 460 core

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoords;

out vsOut
{
vec3 FragPos;
vec3 Normal;
vec3 TexCoords;
vec3 FragPosLightSpace;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;

void main()
{
vs_out.FragPos = model * vec4(aPos, 1.0);
vs_out.Normal = transpose(inverse(mat3(model))) * aNormal;
vs_out.TexCoords = aTexCoords;
vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
gl_Position = projection * view * vec4(vs_out.FragPos, 1.0);
}

FragmentShader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#version 460 core

in vsOut
{
vec3 FragPos;
vec3 Normal;
vec3 TexCoords;
vec3 FragPosLightSpace;
} fs_in;

float shadowCalculation(vec4 fragPosLightSpace)
{
vec3 projCoords = fragPoslightSpace.xyz / fragPoslightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float closestDepth = texture(shadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float shadow = currentDepth > closestDepth? 1.0 : 0.0;
return shadow;
}
阅读全文 »

TBN矩阵和TBN空间

法线贴图采样得到的法线往往在切线空间,也就是顶点的“局部空间”,需要乘以TBN矩阵将法线从切线空间转换到世界空间下才能进行光照计算,而顶点中一般仅提供TBN中的T和N,Bitangent需要经过施密特正交化计算得出:

1
2
3
4
5
6
7
8
void main()
{
vec3 T = normalize(vec3(model * vec4(aTangent, 0.0)));
vec3 N = normalize(vec3(model * vec4(aNormal, 0.0)));
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = mat3(T, B, N);
}
阅读全文 »

辐射度量学

上一章文章我们讨论了颜色, 一个重要结论就是光的颜色和其波长有关, 也就是电磁场的震动频率. 对于人眼的视觉系统来说, 只有很窄范围的波长是可见的, 被称为可见频谱.

01.png

但是渲染不仅仅只是关于颜色, 如果我们只知道一张图片上每个点的波长, 那么呈现的图片将是不完整的, 我们还需要知道多少光命中了该点, 或者说场景中有多少光量进入了摄像机. 从某种意义上说,这两个组件一起构成了我们最终的图像. 每个像素点的颜色是什么,以及强度是多少.

02.png

这就引出了一个问题, 我们如何量化光的测量?

$Radiometry$

$Radiometry$是关于电磁辐射(光)的测量单位和测量体系.其中涉及到了大量的术语,如果从微观角度来看的话, 需要深入掌握大量概念, 但是对于生成图像来说, 我们只需要一个宏观模型, 一个经过调整或建模的适合人类感知的模型.

03.png

阅读全文 »

Computer-Graphic08-空间数据结构

本篇文章我们讨论当场景复杂度大幅增加时,如何进行高效的几何查询。现实世界中的真实场景极其复杂,那么从计算的角度我们如何处理这些复杂的场景呢?其中很重要的一个用例就是光线追踪:用于光线和场景求交。

0.png

光线三角形求交

声明一个三角形,三个顶点分别是$p_0,p_1,p_2$, 定义一条光线,从$o$出发,沿着$d$方向行进,光线的参数化方程可以定义如下:

$\mathbf{r}(t) = \mathbf{o} + t\mathbf{d}$

这里需要解决两个问题,第一个光线是否与三角形相交,第二个如果相交那么交点在哪里?

1.png

首先将光线的参数化方程代入平面的隐式方程可得:

阅读全文 »

颜色

颜色是什么

光线就是震荡的电磁场, 颜色就是频率

01.png

电磁场震荡的频率决定了光线的颜色.

那么光线的波长和频率之间有什么关系呢?

波长和频率其实本质上表征的是一件事情, 但是使用了不同的方式来表达而已.

$wavelength = 1 / frequency$

$frequency = 1 / wavelength$

阅读全文 »

从零实现软渲染(三):Rasterize/绘制三角形

在上一篇文章《窗口系统/viewport》中,通过SDL库我们搭建了一个简易的窗口系统,并通过SDL内置API在屏幕中心绘制了一个矩形来测试RenderLoop。
本章我们将实现自己的渲染器,用它绘制一个三角形。但在开始前,让我们先回顾下渲染的核心概念。

渲染器有两个核心任务:解决可见性着色。解决可见性通常有两种方式:光栅化光追。这两种常用的算法用于解决渲染中的可见性问题, 即找到哪些物体在相机下的视口是可见的.

阅读全文 »

后续文章发布计划

在正式开始本章内容之前,我对专栏后续的更新计划/大纲做了详细梳理,大家可以对整体框架结构及涉及到的知识点有个初步的认识。

在后续的文章更新过程中会基本按照上图框架的内容和顺序逐步展开深入,每一个大的分类代表了渲染系统中的一个组件,正常情况下会做为一篇单独的文章出现,内容过多会拆分为上下两篇(PBR…),另外相对较难或者比较重要的知识点会单独拉出来做为一个专项,预计总共会更新25篇文章左右,从易到难,由浅入深实现整个渲染系统。本系列不涉及图形API,但是shader抽象类会模仿GLSL语言规范的写法和命名方式,使大家有更强的代入感。那么废话不多说,接下来我们正式开始本篇文章的第一个主题:窗口系统的构建。

阅读全文 »

何为软渲染,为什么要学习它

本系列文章将会从零手实现一个mini软渲染器(光栅化), 何谓软渲染? 我们为什么需要学习它? 我们知道复杂的图形渲染一般都是由GPU来处理的,所谓软渲染就是用CPU来模拟GPU的行为,在CPU端完全实现图形的绘制, 这其中包含一系列复杂微妙的过程,但整体上可以将其划分为两个大的模块: 可见性和着色,这其中又包含若干细节:

可见性:

  • 模型变换
  • 视图变换
  • 投影变换
  • 透视除法
  • 视口变换
  • 计算三角形包围盒
  • 三角形光栅化
  • 重心坐标插值
  • 视图变换
  • 深度缓冲
  • 相机模型构建

可见性解决的问题是,对于屏幕上的任意一个像素,场景模型中的哪个片元对它是可见的. 主要通过以下几个步骤完成:

阅读全文 »

截止到目前我们学习了如何将物体拜访到世界空间,如何将三维空间物体投影到二维屏幕上,以及对三角形光栅化,判断哪些像素被三角形所覆盖,接着基于重心坐标对三角形三个顶点插值得到采样点属性,拿到采样点片元的$uv$后可以对纹理采样, 这个过程涉及纹理过滤(双线性插值,最临近采样...)及$Mipmap$等,最后通过深度及透明度将这些片元混合输出到帧缓冲($FrameBuffer$)

阅读全文 »
0%