【Unity Shader】标准光照模型总结

逐顶点与逐像素光照
逐顶点光照[per-vertex lighting] [vertex shader]
在每个顶点上计算光照,然后会在渲染图元内部进行线性插值,最后输出成像素颜色,由于顶点数目往往远小于像素数目,因此逐顶点光照的计算量往往要小于逐像素光照。
逐像素光照[per-pixel lighting][fragment shader]
以每个像素为基础,得到它的法线(可以是对顶点法线插值得到的,也可以是从法线纹理中采样得到的),然后进行光照模型的计算。这种在面片之间对顶点法线进行插值的技术被称为Phong 着色(Phong shading)。

【Unity Shader】标准光照模型总结_第1张图片

左侧逐像素光照,右侧为逐顶点光照。可以看到逐顶点光照的缺点:被光面与向光面交界处有锯齿。

逐顶点光照与逐像素光照间的区别:
candycat:
1.由于逐顶点光照依赖于线性插值来得到像素光照,因此,当光照模型中有非线性的计算(例如计算高光反射时),逐顶点光照就会出问题。
2.由于逐顶点光照会在渲染图元内部对顶点颜色进行插值,这会导致渲染图元内部的颜色总是暗于顶点处的最高颜色值,这在某些情况下会产生明显的棱角现象。
puppet_master:
为什么逐像素计算会得到更好的效果,因为我们逐像素取的光照的方向是一致的,法线方向也是通过上一步的vertex shader传递过来的,如果像素和顶点对应了的话,那不是每个像素的计算结果都会一样呢?然而,其实像素和顶点是不对应的,这个就是传说中的渲染流水线了,在顶点阶段计算的结果,并不是直接传递给像素着色器的,而是经过了一系列的插值计算,我们从vertex shader传递过来的法线方向,只代表了这一个顶点的顶点法线方向,而到了pixel阶段,这个像素所对应的法线等参数相当于其周围几个顶点进行插值后的结果。我们用这一个像素点对应的法线方向与光照方向进行计算,就可以获得该像素点在光照条件下的颜色值,而不是先计算好颜色再插值得到结果。
coffeecato:这也就解释了为什么法线在vert不能进行归一化

v2f vert(a2v v)
{
    //把法线转化到世界空间
    //o.worldNormal = mul(v.normal, (float3x3)_World2Object);
    o.worldNormal = UnityObjectToWorldNormal(v.normal);
}

fixed4 frag(v2f i) : SV_Target
{
    //归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的
    fixed3 worldNormal = normalize(i.worldNormal);
}


高光贴图的由来

【Unity Shader】标准光照模型总结_第2张图片

左侧为Blinn-Phong光照模型,可以看出Blinn-Phong光照模型的高光反射部分看起来更大、更亮一些。

puppet_master:
现实世界中漫反射和镜面反射是共存的,拿一把刀来说,刀身是金属,刀柄是木头,那么,只有刀身适合这种类型的shader。可能我们最简单的想法是把刀拆成两个部分,刀身用的是Specular,刀柄用Diffuse;但是这种做法很麻烦,而且一个物体通过了两个drall call才能渲染出来。所以,聪明的前辈们总是能想到好的办法,次时代类型游戏中最简单的一种贴图就诞生了---高光贴图(通道)。
所谓高光贴图,或者说成高光通道,就是通过在制作贴图时,把图片的高光信息存储在一个灰度图或者直接存储在贴图的通道内,如果不需要Alpha Test的话,可以直接把高光通道放在Diffuse贴图的Alpha通道。而我们在shader中计算时,通过采样,就可以获得这个贴图中每个像素对应的位置是否是有高光的。这样,在Fragment Shader中可以直接通过这个Mask值乘以高光从而通过一个材质渲染出同一个模型上的不同质地。比如在通道内,0表示无高光,1(255)表示高光最强,那么,不需要高光的地方我们就可以在制作这张图的时候给0,需要高光的,刷上颜色,颜色越接近白色,高光越强。
candycat:
高光贴图其实是一种遮罩纹理(mask texture),遮罩允许我们保护某区域,使他们免于修改或者增强(减弱)。使用遮罩纹理的流程一般是:通过采样得到遮罩纹理的纹素值,然后使用其中某个(或某几个)通道的值来与某种表面属性进行相乘。
 

环境光对模型的影响
puppet_master:
但是在实时图形学,我们大部分情况是通过一个环境光(Ambient Light)统一代表了间接光,这样,即使在没有光的时候,我们也可以看见物体。
https://blog.csdn.net/puppet_master/article/details/53074789
candycat:
大多数物体是没有自发光特性的,因此在本书绝大部分的shader中都没有计算自发光部分。如果要计算自发光,只需在片元着色器输出最后的颜色之前,把材质的自发光颜色添加到输出颜色上即可。
coffeecato:
frag返回最终颜色时,如果去掉ambient(环境光)关掉灯光模型会变成黑色

fixed3 color = diffuse + ambient + specular;        // 关闭灯光 模型不会变黑
fixed3 color = diffuse + specular;                  // 关闭灯光 模型会变黑

本文截图的所有代码都可以从参考资中找到,puppet_master是位做图形的同学,他的文章很适合入门,推荐关注。

参考资料:

《Unity Shader入门精要》

Unity Shader-兰伯特光照模型与Diffuse Shader

Unity Shader-Phong光照模型与Specular

你可能感兴趣的:(Unity3d,Shader,游戏开发)