如何在Unity中实现高光反射

217 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,[点击查看活动详情]

前言

在之前的文章中,对漫反射光照模型进行了介绍,核心是利用兰伯特定律来描述。本篇介绍光照模型中的高光反射(也就是我们经常说的镜面反射),同样使用Unity游戏引擎来实现。

高光反射的由来

在介绍高光反射之前,我们先来看看当一束光线从光源发出,照射到物体表面的这一系列过程都发生了什么?光线发出会跟物体发生相交,而相交的结果有两种:散射和吸收。这里吸收我们暂时不考虑。光线经过物体表面散射之后,也会有两种结果:即要么散射到物体内部,要么散射到物体的外部。散射到物体内部的现象被称为折射,而到外部的部分就形成了反射。高光反射就是用来描述散射到物体外部的这一部分辐射量的。

利用Phone模型计算高光

知道了高光反射的由来,最后需要将其转换为一个数学问题。这个时候就该Phone模型上场了:

specular = lightColor * specularColor * max(0,v·r)^gloss //gloss为光泽度,可以理解为一个常数

从公式我们可以看出模型需要的信息包括观察方向和反射方向,而反射方向的计算方式又需要光源方向和物体表面的法线方向,所以为了利用Phone模型来描述高光反射,我们总共需要四个信息,即物体表面法线、观察方向、光源方向、反射方向。

高光反射.jpg

在unity中的具体实现

有了前面的理论知识之后,来看看在Unity引擎中如何具体实现。

  • 1、在Project面板右键创建一个Unlit Shader,将其命名为Specular。

16708316815892.jpg

  • 2、选中刚刚创建的Specular,右键创建一个Material。

16708317636503.jpg

  • 3、在Hierarchy面板创建一个Sphere,并将第2步创建的Material拖拽赋值到Sphere上。

16708319644552.jpg

  • 4、使用脚本IDE打开第一步创建的Shader,添加代码如下:
Shader "Unlit/Specular"
{
    Properties
    {
        //漫反射颜色
        _Diffuse("Diffuse",Color)=(1.0,1.0,1.0,1.0)
        //高光颜色
        _Specular("Specular",Color)=(1.0,1.0,1.0,1.0)
        //光泽度 值越大高光点越小
        _Gloss("Gloss",Range(1,90)) = 20
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"

            float4 _Diffuse;
            float4 _Specular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 posWS:TEXCOORD0;
                float3 nDirWS:TEXCOORD1;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.posWS = mul(unity_ObjectToWorld,v.vertex);
                o.nDirWS=UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 nDirWS = i.nDirWS;
                float3 lDirWS = _WorldSpaceLightPos0.xyz;
                float3 vDirWS = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
                float3 hDirWS = normalize(vDirWS + lDirWS);
                float3 rDir = reflect(-lDirWS,nDirWS);
                float nDotl = dot(nDirWS,lDirWS);
                float lambert = max(0,nDotl);
                //构建Phong光照模型
                float3 ambient  = UNITY_LIGHTMODEL_AMBIENT.xyz;
                float3 diffuse = _LightColor0.rgb *   _Diffuse.rgb * lambert;
                float vDotr = dot(vDirWS,rDir);
                float phone = _LightColor0.rgb * _Specular.rgb * pow(max(0,vDotr),_Gloss);
                float3 col = ambient + diffuse + phone;
                return fixed4(col,1.0);
            }
            ENDCG
        }
    }
}

  • 5、查看效果。

16708347884441.jpg

最后

Shader中的颜色参数设置比如漫反射颜色,高光颜色,光泽度等参数可以在材质面板中自由设置。本篇主要介绍了Phone光照模型中的高光是如何描述的。OK,本篇就到这里。