Shader学习三(基础光照)

漫反射

这一节主要讲逐顶点漫反射,先讲Unity中的基础光照,从平行光和点光源,多个光源开始,后面讲高光反射和逐像素光照和双面光照。

漫反射

       在理想化的漫反射的情况下,我们看到的光照强度和物体表面法线向量和入射光的的夹角的余弦有关,表面的法线向量垂直于表面,入射光的方向为点到光源的方向,我们可以使用两个向量的点积得到的值来表示夹角的余弦,因为向量a和向量b的点积等于两个向量的模乘以向量a和向量b的余弦值,因为我们使用的是标准向量,所以向量的模都是1,则两个向量的点积就是夹角的余弦值,根据这个余弦值我们可以判定光照强度,这个值越大,则光照越强,越小则越弱,如果小于0,那就是没有反射光。

  1. 单一光源下的代码实现

       如果我们只有一个光源,实现光照的代码很简单,为了实现灯饰,我们需要讨论下面的几个问题:

  • 老问题,代码写在哪?顶点函数还是片元函数?
  • 在哪个坐标系统下做等式计算?Unity默认的是世界坐标系统
  • 从哪里得到我们需要的参数?

       我们需要定义一个Shader变量来获取用户定义的材质颜色的漫反射系数,光源位置我们可以从Unity内置的变量(_WorldSpaceLightPos0)获取到;入射光照的颜色可以通过(_LightColor0)来获取,要记住,我们要标记光照模式为ForwardBase来保证上面的两个变量的值是正确的;我们再顶点输入参数中获取到模型坐标系下物体表面的法线向量(NORMAL类型数据);如果我们再世界坐标系下实现该效果,我们==需要将表面的法线向量从模型空间转换到世界空间下==。

Tags {"LightMode" = "ForwardBase"}
    struct vertexInput {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };
    struct vertexOutput {
        float4 pos : SV_POSITION;
        float4 col : COLOR;
    };
    vertexOutput vert(vertexInput input)
    {
        vertexOutput output;
        float4x4 modelMatrix = _Object2World;
        float4x4 modelMatrixInverse = _World2Object;
        // multiplication with unity_Scale.w is unnecessary
        // because we normalize transformed vectors
        float3 normalDirection = normalize(float3(
        mul(float4(input.normal, 0.0), modelMatrixInverse)));
        float3 lightDirection = normalize(
        float3(_WorldSpaceLightPos0));
        float3 diffuseReflection =
        float3(_LightColor0) * float3(_Color)
        * max(0.0, dot(normalDirection, lightDirection));
        output.col = float4(diffuseReflection, 1.0);
        output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
        return output;
    }
    float4 frag(vertexOutput input) : COLOR
    {
        return input.col;
    }

       使用上面的Shader的时候,要确定场景中只有一个(directional)方向光源,如果场景中没有光源,可以自己创建一个,另外,还需要将菜单下 ==Edit>Project Settings>Player== 然后打开Inspector View ,然后Per-Platform Settings -> Other Settings -> Rendering -> Rendering Path 应该为Forward;(当然这个设置是默认的)

  1. 多方向光模式的代码实现

       到目前为止,我们只学习了单一光源,为了操作多个光源,Unity有很多渲染和质量的各种技术配置,在这,我们只讨论Forward Rendering Path;
这里需要介绍一下像素光(也是一种方向光),Unity调用Shader的Pass标签Tags{"LightMode" = "ForwardBase"},对于每个像素光,Unity都会调用Tags{"LightMode" = "ForwardAdd"}。为了确定所有的光都按照像素光来渲染,我们需要设置一下Edit->Project Setting Quality 然后把Pixel Light Count 的值增加到你用的数值。如果你增加的像素光大于这个值的最大值,Unity值会渲染你添加的这么多光的比较重要的那一部分,你可以设置光照组件的Render Mode属性为Important;那么Unity就会有限渲染这个;到目前为止,我们的Shader代码是OK的,在ForwardAdd这个Pass块中,我们需要将反射光加到已经被储存在帧缓冲中的光照上,也就是说我们需要将片元输出的颜色叠加到帧缓冲中的颜色;就是我们再上一章节中说的透明中的一个Additive混合模式,使用的公式为 Blend One One

  1. 点光源
           如果是方向光, _WorldSpaceLightPos0指定了方向光的==方向==,但是如果是点光源的话, _WorldSpaceLightPos0就是点光源的==位置==,如果想得到光照方向,我们需要计算顶点在世界坐标中的位置到光源位置的方向。我们能够很容易的通过光源坐标的第四个值是否等于0来分辨到底是方向光还是点光源:
float3 lightDirection;
if (0.0 == _WorldSpaceLightPos0.w) // 方向光,代表的是光的方向
{
    lightDirection = normalize(float3(_WorldSpaceLightPos0));
}
else // 点光源或者聚光灯,代表的是光源的坐标
{
    lightDirection = normalize(float3(_WorldSpaceLightPos0 - mul(modelMatrix, input.vertex)));
}

       然而方向光是没有光照减弱的,对于点光源和聚光灯,我们就需要增加一些随着距离的变化,光照强度的减弱;光照有个三个方向从一个点发射并传播出去的,只要有足够的距离,它就能够完全覆盖照射一个足够大的虚拟球体;我们知道球的表面积是和半径的平方成正比的,每个球上的灯光的数量是一样的,单位面积下的灯光数量是和半径的平方成反比的;因此,我们应该根据顶点和光源的距离的平方来改变光照的强度
       由于二次方的衰减太快了,我们使用一个方法根据距离来进行线性的衰减;我们使用距离来替代距离的平方;代码如下:

float3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
    attenuation = 1.0; // no attenuation
    lightDirection = normalize(float3(_WorldSpaceLightPos0));
}
else // point or spot light
{
    float3 vertexToLightSource = float3(_WorldSpaceLightPos0
    - mul(modelMatrix, input.vertex));
    float distance = length(vertexToLightSource);
    attenuation = 1.0 / distance; // linear attenuation
    lightDirection = normalize(vertexToLightSource);
}

       然后那个减弱因子需要乘以_LightColor0后计算处入射光的颜色和强度,聚光灯有增强效果,但是这里超出了本章节讨论的范围, 下面的代码的性能不太好,因为任何的if语句都是很消耗的,因为需要判定_WorldSpaceLightPos0的w分量是等于0还是1;事实上我们很难去重写代码去避免这个if判定语句去优化很多。
       注意:光源在ForwardBase模式下都是方向光,因此我们的第一个Pass块很简单,另一方面,在两个Pass块中使用相同的CG代码,让我们更容易复制和粘贴然后再去修改Shader代码。

Specular Highlights(镜面突出显示)

这一小节覆盖了用Phong反射模型进行逐顶点光照。扩展了==漫反射增加了两个部分:环境光和高光反射,这三个在一起构成了Phong反射模型==;

  • 环境光

比如我们看一幅画,尽管白色的衣服很大一部分都在阴暗处,但是衣服没有一个地方是完全黑的,因为总是会有其他物品反射的光到衣服上;在Phong反射模型中,这个效果是有环境光得到的,环境光以来一个光照强度I和漫反射得到的材料的颜色K;计算环境光寡照强度的公式如下:

I_ambient = I_ambient light K_diffuse

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容