Unity前向渲染路径的光照总结实现

        勿于浮沙筑高台。回顾总结一下Unity前向渲染路径。

一、前置知识:光照的基本框架

        光照主要分为:直接光照、间接光照、(环境光是间接光的一部分)。

        这其中直接光照占主导地位。而为了减少间接光照的计算,有很多种技术来表达间接光照。

比如:LightMap,Reflection probe(反射球,IBL基于图像的照明),Light Probe(球谐光照SH)

        目前的光照计算中最完整的是BRDF(Bidirectional Reflectance Distribution Function)光照模型。但是这个模型较为复杂,所以有一些简化后的光照模型。比如:标准光照模型

二、标准光照模型(Phong光照模型)

        只关心直接光照。把进入摄像机的光线分为4个部分:

        i.自发光emissive

        ii.漫反射diffuse。

                Lambert定律, half Lambert定律。

        以Lambert为例,就是求光照方向与法线夹角的cos值,注意这里的光照方向是从顶点指向光源,这里只是约定俗称的。

       

        iii.高光反射specular

                       Phong光照模型和Blinn-Phong光照模型。

        以Phone光照为例,这里是求反射光与视线的夹角的cos值。smoothness值来控制光线的衰减,也就是光斑的大小。

        iv.环境光ambient(表示所有的间接光照)。

        具体实现在实现前向渲染的光照shader中实现。

三、前向渲染路径

       渲染路径是决定灯光数据传递方式的。

        1.实时光的处理

        对于前向渲染,在场景里有多少盏实时灯光。物体就会被渲染多少次。而在含光照的shader中,分为两个Pass。

        一个Pass为ForwardBase。这个Pass主要渲染主1盏平行光(逐像素)(以及小于等于4盏的顶点光,和SH球谐光。)

        另一个Pass为ForwardAdd,这个Pass里面会渲染其他逐像素光源(逐像素)。多少盏由下面这个设置决定。

        其他光源的处理方式:1.ForwardBase的顶点光。2.SH球谐光照

      光源重要等级排序:forwardBase像素光源 > forwardAdd像素光源 > forwardBase顶点光源 > SH球谐光源

   2.怎么区分不同的光源

                1.RenderMode的设置。Not Important 的光源为顶点光或 SH 光源。Important 的光源为每像素光源。

                2.按照光的强度排序。

如果像素光源少于设置,还有其他光源的话,其他光源可以依次升级。同理,像素光源多了就会依次降级。

四、实现简单的光照Shader

        使用phong光照模型和Lambert光照模型。

Shader "Awen_Unlit/LambertPhoneSimple"
{
    Properties
    {
		//最原始的数值上的光照模型
		_Shininess("Shininess",Range(0.01,100)) = 1.0
		_AmbientColor("Ambient Color",COLOR) = (0.0,0.0,0.0,0.0)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
			Tags{ "LightMode" = "ForwardBase"}

			CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
			#pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
			#include "AutoLight.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
				float3 normal_dir : TEXCOORD1;
				float3 pos_world : TEXCOORD2;
            };

			float4 _LightColor0;//定义了才能拿到光照颜色
			float _Shininess;//光照衰减
			float4 _AmbientColor;//环境光单纯使用的纯色

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;

				o.normal_dir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
				o.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;

				//o.tangent_dir = normalize(mul(float4(v.tangent,0.0),))


                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
				half3 normal_dir = normalize(i.normal_dir);
				//观察方向
				half3 view_dir = normalize(_WorldSpaceCameraPos - i.pos_world);
                //平行光的灯光方向
				half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);
				
				//Lambert漫反射
				half NdotL = dot(normal_dir,light_dir);
				//获取灯光颜色,需要定义_LightColor0变量
				half3 diffuse_color = max(0.0,NdotL) * _LightColor0.xyz;

				//Phone高光
				//注意这里的光照方向要加负号,因为灯光方向求的是顶点指向灯光
				half3 reflect_dir =  reflect(-light_dir,normal_dir);
				half RdotV = dot(reflect_dir,view_dir);
				half3 spec_color =  pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz;

				half3 final_color = diffuse_color + spec_color + _AmbientColor.xyz;
				// sample the texture
               // fixed4 col = tex2D(_MainTex, i.uv);
              
                return float4(final_color,1.0);
            }
            ENDCG
        }
		Pass
        {
			Tags{ "LightMode" = "ForwardAdd"}
			Blend One One
			CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
			#pragma multi_compile_fwdadd

            #include "UnityCG.cginc"
			#include "AutoLight.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
				float3 normal_dir : TEXCOORD1;
				float3 pos_world : TEXCOORD2;
            };

			float4 _LightColor0;//定义了才能拿到光照颜色
			float _Shininess;//光照衰减
			float4 _AmbientColor;//环境光单纯使用的纯色

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;

				o.normal_dir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
				o.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;

				//o.tangent_dir = normalize(mul(float4(v.tangent,0.0),))


                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
				half3 normal_dir = normalize(i.normal_dir);
				//观察方向
				half3 view_dir = normalize(_WorldSpaceCameraPos - i.pos_world);

				#if defined (DIRECTIONAL)
				half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);//平行光
				half attenuation = 1.0;
				#elif defined (POINT)
				half3 light_dir = normalize(_WorldSpaceLightPos0.xyz - i.pos_world);
				half distance = length(_WorldSpaceLightPos0.xyz - i.pos_world);
				half range = 1.0 / unity_WorldToLight[0][0];
				//最原始简单的点光源计算衰减值的做法,现在Unity是使用渐变图来计算的衰减
				half attenuation = saturate((range - distance)/ range);
				#endif
				//Lambert漫反射
				half NdotL = dot(normal_dir,light_dir);
				//获取灯光颜色,需要定义_LightColor0变量
				half3 diffuse_color = max(0.0,NdotL) * _LightColor0.xyz * attenuation;

				//Phone高光
				//注意这里的光照方向要加负号,因为灯光方向求的是顶点指向灯光
				half3 reflect_dir =  reflect(-light_dir,normal_dir);
				half RdotV = dot(reflect_dir,view_dir);
				half3 spec_color =  pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz * attenuation;

				half3 final_color = diffuse_color + spec_color + _AmbientColor.xyz;       
                return float4(final_color,1.0);
            }
            ENDCG
        }
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值