美文网首页
Shader学习三(基础光照)

Shader学习三(基础光照)

作者: LazyBoy | 来源:发表于2018-07-10 09:41 被阅读0次

    漫反射

    这一节主要讲逐顶点漫反射,先讲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
    
    

    相关文章

      网友评论

          本文标题:Shader学习三(基础光照)

          本文链接:https://www.haomeiwen.com/subject/okxfpftx.html