Unity3D Shader之光照模型——理论与实践:用两种方式来实现漫反射Diffuse Reflection

来源:互联网 发布:冒险岛交易市场数据 编辑:程序博客网 时间:2024/05/16 15:58

漫反射原理解析

众所周知,漫反射即光线射到粗糙的物体表面时发生的反射现象,入射光线为平行光线,而出射光线为各个方向。光在空气中以直线传播,光线的反射也遵守物理规则。如下图所示:

这里写图片描述
图片来源于网络。

上图左侧入射方向的是平行光,反射光也是平行光,这种现象称为镜面反射。而左侧反射光不平行无规则的则为漫反射。

这里写图片描述这里写图片描述
图片来源于网络。

上图左侧表示,入射光线与出射光线形成的夹角,被垂直于反射面的光线均分。上图右侧表示在圆弧或不规则表面时,反射平面为反射点的切线,此时反射点的法线为经过此点垂直于其切线的直线。

然而在图形处理中,实际情况是模型远远没有现实这么复杂,有的时候甚至想用简单的模型(甚至是完全平面的模型)来表现出复杂或是特殊材质的物体。对于漫反射而言,不可能真的去做出具有无数个顶点,表面具有极其细微的凹凸不平之处的模型,如果真这样做,无论是人力还是计算机的处理能力都是非常有限的。因此就要使用一定的算法来模拟漫反射。

根据反射的规则和实际生活情况可知,入射光线与反射光线的夹角越小,则反射强度越大。并且物体表面越粗糙,那么反射后的亮度越弱,这是因为粗糙的表面将光线反射到了各个地方去。因此,我们要模拟的就是:

  • 反射规则:入射光线与反射光线的关系
  • 光线逸散:反射后由物体表面粗糙的原因导致光线光线逸散,反射后光线亮度变低

结合上两点,在实际生活中观察到的漫反射现象是: 入射光线与法线的夹角越小,则反射后的光线亮度越大。于是便采用 入射光线与法线的点积来 来计算反射系数,因为点积的几何意义是:点积的值越大,则两个向量的方向越相近。


结合我前面的文章《Unity3D内建参数文档翻译与解析——Built-in shader variables》来使用两种不同的Shader形式实现漫反射。

可编程Shader实现方式

代码

Shader "Custom/DiffuseReflection"{    Properties    {        _Color("Color", Color) = (1,1,1,1)        _MainTex("Texture", 2D) = "white" {}    }    SubShader    {        // 设定LightMode为ForwardBase,相关细节可以查看我之前的文章《Unity3D光照前置知识——Rendering Paths(渲染路径)及LightMode(光照模式)译解》        Tags{ "LightMode" = "ForwardBase" }        Pass        {            CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #include "UnityCG.cginc"            // 使用Unity3D的头文件"Lighting.cginc",包含有我们需要的光照参数            #include "Lighting.cginc"            struct appdata            {                float4 vertex : POSITION;                float2 uv : TEXCOORD0;                half3 normal : NORMAL;            };            struct v2f            {                float2 uv : TEXCOORD0;                float4 vertex : SV_POSITION;                float4 diff : TEXCOORD1;                float4 diffColor : TEXCOORD2;            };            v2f vert(appdata v)            {                v2f o;                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);                o.uv = v.uv;                // 本例中将光照计算过程写在vert函数内,因为这样相比写在frag函数内更节约性能。不过当然效果肯定没有在frag内好。                // 计算物体法线(用来代表在物体上入射点处的法线)与光照方向的点积,得到漫反射系数diff,用来表示漫反射的光照衰减,并且保证其值大于0。                float diff = max(0, dot(v.normal, _WorldSpaceLightPos0.xyz));                // 利用系数计算光线衰减后的色值                o.diffColor = _LightColor0 * 2 * diff;                return o;            }            sampler2D _MainTex;            fixed4 _Color;            fixed4 frag(v2f i) : SV_Target            {                fixed4 col = tex2D(_MainTex, i.uv) * _Color;                // 得到最终颜色                col.rgb = i.diffColor * col.rgb;                return col;            }            ENDCG        }    }}

结果

这里写图片描述


Surface Shader实现方式

代码

surface shader是Unity3D特有的一种Shader编写形式,封装了许多光照相关的功能。以后会开坑细讲。

Shader "Custom/DiffuseReflection" {    Properties {        _Color ("Color", Color) = (1,1,1,1)        _MainTex ("Albedo (RGB)", 2D) = "white" {}    }    SubShader {        Tags { "RenderType"="Opaque" }        LOD 200        CGPROGRAM        // 声明自定义光照函数名        #pragma surface surf DiffuseReflection        #pragma target 3.0        sampler2D _MainTex;        struct Input {            float2 uv_MainTex;        };        fixed4 _Color;        // 自定义光照函数        half4 LightingDiffuseReflection(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)        {            half diff = max(0, dot(s.Normal, lightDir));            half3 c = s.Albedo * diff * _LightColor0.rgb * 2;            return fixed4(c, s.Alpha);        }        void surf (Input IN, inout SurfaceOutput o) {            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;            o.Albedo = c.rgb;            o.Alpha = c.a;        }        ENDCG    }    FallBack "Diffuse"}

结果

这里写图片描述

0 0
原创粉丝点击