US3S1L8——逐片元比逐顶点更光滑的原因

知识回顾

渲染管线回顾:

image

通过知识点回顾,我们知道在顶点着色器和片元着色器之间还存在其他流程,比如:

  1. 裁剪
  2. 屏幕映射
  3. 三角形设置
  4. 三角形遍历

image

等等

也就是说顶点着色器回调函数中返回的数据,还会在这些中间流程中进行处理,再传递给片元着色器。这也是为什么逐片元比逐顶点更加平滑的原因

逐片元比逐顶点更光滑的原因

  1. 为什么逐顶点光照的渲染表现比较粗糙

    顶点以外的像素点颜色,是进行插值运算的结果,效果不真实

  2. 为什么逐片元光照的渲染表现更加的平滑

    每一个像素点都利用其相关数据(法线、位置等)进行颜色计算,效果更真实

注意:也侧面说明了片元着色器的性能消耗更大,顶点着色器的性能消耗更小

为何逐顶点光照比较粗糙

如果我们在顶点着色器中进行了颜色相关的计算,我们只是计算了​**模型顶点位置的颜色信息**。

1
2
3
4
5
6
7
8
9
10
11
v2f vert (appdata_base v)
{
v2f v2fData;
v2fData.svPos = UnityObjectToClipPos(v.vertex); //首先将模型空间下的顶点转换到裁剪空间下
//计算Phong式光照模型需要的各种颜色
fixed3 lambertColor = getVertLambertColor(v.normal); //计算漫反射
fixed3 specularColor = getVertSpecularColor(v.vertex, v.normal); //计算高光反射颜色
v2fData.color = UNITY_LIGHTMODEL_AMBIENT.rgb + lambertColor + specularColor; //计算Blinn-Phong式光照模型颜色

return v2fData;
}

假设有一个三角面片,计算得到顶点A为蓝色,顶点B为绿色,顶点C为红色:

image

这些数据在渲染管线中传递时,会在光栅化阶段的片元着色器之前对三角形围着的片元颜色进行插值运算,再传递给片元着色器。

也就是说顶点之间的片元颜色信息,其实都不是使用该位置的相关信息计算的,
而是粗暴的插值计算,计算颜色的平均值,那么自然呈现出来的效果是比较粗糙的

image

例如,假设三角形中间有一个围起来的片元P,它的颜色就是由三个顶点A,B,C的颜色插值计算出来的,
而不是通过片元P位置坐标,法线相关信息计算出来的,自然这种颜色的表现效果是粗糙的。

为何逐片元光照更加平滑

首先我们要明确的是,片元着色器中传入的参数结构虽然和顶点着色器中返回的一样,
但是里面的数据是不同的,片元着色器中传入的数据,都是为每一个片元进行专门插值计算后的结果
因此我们利用这些数据再次进行光照计算,自然更加的平滑真实。

也就是说,片元着色器传入的是经过差值计算后,各片元自己的位置坐标,法线等相关信息,而非三角面片的顶点

1
2
3
4
5
6
7
8
9
10
//这里传入的v2f和顶点着色器返回的v2f,结构相同,但数据不同!!!
fixed4 frag (v2f i) : SV_Target
{
//计算Blinn-Phong式光照模型需要的各种颜色
fixed3 lambertColor = getFragLambertColor(i.wNormal); //计算漫反射
fixed3 specularColor = getFragSpecularColor(i.wPos, i.wNormal); //计算高光反射颜色
fixed3 blinnPhongColor = UNITY_LIGHTMODEL_AMBIENT.rgb + lambertColor + specularColor; //计算Blinn-Phong式光照模型颜色

return fixed4(blinnPhongColor.rgb, 1); //因为传递过来的颜色变量不包括透明度,因此这里需要手动指定透明度
}

这样,在片元着色器中利用光照模型计算颜色信息,实际上使用的是各片元的坐标,法线信息

也就是说,在片元着色器中进行颜色相关计算时,
能够为每一个像素点,基于它的相关数据单独进行颜色计算,
而不是直接利用插值颜色,自然最终的表现效果会更加的平滑真实。

image

例如,假设三角形中间有一个围起来的片元P,在片元着色器中,就会使用片元P的位置坐标,法线相关信息计算颜色

通过片元自己的坐标,法线信息去使用光照模型计算颜色,显然,会比直接使用顶点的颜色差值计算的效果要细腻,更接近期望的光照效果