unity Build-in 光源和光照模型

 1、Unity光源模型

1.1 两种渲染方式

前向渲染:每个物体都有可能被重复渲染,即基于物体的光源渲染;超出灯光设置数量被当作顶点光(6*6)

延迟渲染:基于光源的物体渲染;以灯光为单位,先计算灯光所需东西,如RT0为漫反射,RT1为金属度,RT2为法线图;对于多光源友好(6+6);

区别:延迟渲染需要MRT技术(multi render target)和空间容量大

1.2 前向渲染

Unity两种前向渲染管线

bulid in:跟上文提到的概念一样;base+add_{x}  个pass;base仅一个方向光。

urp:一个pass,最多八盏灯;

1.3 Phong光源模型 

高光反射:pow(RdotV,smoothness)×lightcolor;

漫反射:NdotL×lightcolor

若附加纹理,相乘即可。

1.4 法线贴图

unity中的切线a分量是为了兼容各平台差异

法线贴图需要解码,解码后调整隆起大小再转至TBN空间

反射为什么灯光要负方向?

因为unity内置的计算灯光方向是从模型向光源走

forwardbase的实现:

Shader "Unlit/phong2"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _smoothness("Smoothness",Range(0.01,100))=1.0
        _specuIntensity("SpecuIntensity",Range(0.00,100))=1.0
        _NormalTex ("NormalTex", 2D) = "white" {}
        _normalIntensity("NormalIntensity",Range(0.00,5))=1.0

    }
    SubShader
    {
        
        LOD 100

        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 nor:NORMAL;
                float4 tangent:TANGENT;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 posW:TEXCOORD1;
                float3 norW:TEXCOORD2;
                float3 tangentW:TEXCOORD3;
                float3 biNormal:TEXCOORD4;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _LightColor0;
            float _smoothness;
            float _specuIntensity;
            float _normalIntensity;
            sampler2D _NormalTex;
            float4 _NormalTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.posW=mul(unity_ObjectToWorld,v.vertex);
                o.norW=normalize(mul(float4(v.nor,0.0),unity_ObjectToWorld).xyz);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                
                o.tangentW=normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz)*v.tangent.w;
                o.biNormal=normalize(cross(o.tangentW,o.norW));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half3 final;
                //normal map
                float4 normalmMap=tex2D(_NormalTex,i.uv);
                float3 normal=UnpackNormal(normalmMap).xyz;
                normal.xy=normal.xy*_normalIntensity;
                float3 tangentW=normalize(i.tangentW);
                float3 biNormal=normalize(i.biNormal);
                float3 norW=normalize(i.norW);
                float3x3 TBN=float3x3(tangentW,biNormal,norW);
                normal=normalize(mul(normal,TBN)) ;
                // ambient
                fixed4 baseColor = tex2D(_MainTex, i.uv);
                half3 ambient=baseColor.rgb*UNITY_LIGHTMODEL_AMBIENT.rgb;

                //DIFFUSE
                //float3 normalW=normalize(i.norW);
                float3 L=normalize(UnityWorldSpaceLightDir(i.posW));
                float NdotL=saturate(dot(L,normal));//
                half3 diffuse=NdotL*baseColor.rgb*_LightColor0.xyz;

                //SPECULAR
                float3 R=reflect(-L,normal);//
                float3 viewDirW=normalize(UnityWorldSpaceViewDir(i.posW));
                float3 specu=pow(saturate(dot(R,viewDirW)),_smoothness)*_LightColor0.xyz*_specuIntensity;
                final=diffuse.rgb+ambient.rgb+specu.rgb;

            
                return half4(final,1.0);
            }
            ENDCG
        }
    }
}

forwardadd的实现:

Tags { "LightMode"="ForwardAdd" }
            Blend One One
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdadd
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 nor:NORMAL;
                float4 tangent:TANGENT;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 posW:TEXCOORD1;
                float3 norW:TEXCOORD2;
                float3 tangentW:TEXCOORD3;
                float3 biNormal:TEXCOORD4;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _LightColor0;
            float _smoothness;
            float _specuIntensity;
            float _normalIntensity;
            sampler2D _NormalTex;
            float4 _NormalTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.posW=mul(unity_ObjectToWorld,v.vertex);
                o.norW=normalize(mul(float4(v.nor,0.0),unity_ObjectToWorld).xyz);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                
                o.tangentW=normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz)*v.tangent.w;
                o.biNormal=normalize(cross(o.tangentW,o.norW));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half3 final;


                //光源衰减
                #if defined(DIRECTIONAL)
                float3 L=normalize(UnityWorldSpaceLightDir(i.posW));
                float  attenuation=1.0;
                #elif defined(POINT)
                float3 L=normalize(_WorldSpaceLightPos0.xyz-i.posW);
                float distance=length(_WorldSpaceLightPos0.xyz-i.posW);
                float Range=1.0/unity_WorldToLight[0][0];
                float  attenuation=saturate(Range-distance/Range);
                #endif
                //光源衰减

                //normal map
                float4 normalmMap=tex2D(_NormalTex,i.uv);
                float3 normal=UnpackNormal(normalmMap).xyz;
                normal.xy=normal.xy*_normalIntensity;
                float3 tangentW=normalize(i.tangentW);
                float3 biNormal=normalize(i.biNormal);
                float3 norW=normalize(i.norW);
                float3x3 TBN=float3x3(tangentW,biNormal,norW);
                normal=normalize(mul(normal,TBN)) ;
                // ambient
                fixed4 baseColor = tex2D(_MainTex, i.uv);
                half3 ambient=baseColor.rgb*UNITY_LIGHTMODEL_AMBIENT.rgb;

                //DIFFUSE
                //float3 normalW=normalize(i.norW);
                
                float NdotL=saturate(dot(L,normal));//
                half3 diffuse=NdotL*baseColor.rgb*_LightColor0.xyz;

                //SPECULAR
                float3 R=reflect(-L,normal);//
                float3 viewDirW=normalize(UnityWorldSpaceViewDir(i.posW));
                float3 specu=pow(saturate(dot(R,viewDirW)),_smoothness)*_LightColor0.xyz*_specuIntensity;
                final=(diffuse.rgb+specu.rgb)*attenuation;

            
                return half4(final,1.0);
            }

点光源衰减范围:r-d/r

bliphong,在phong模型基础上优化高光反射,减少消耗,(半角向量)h=l+v,NdotH

// phong
    // float3 R=reflect(-L,normal);
    // float RdotN=saturate(dot(R,norW));
    // float3 specu=pow(RdotN,_smoothness)*_LightColor0.xyz*_specuIntensity;
                
//bliphong
    float3 viewDirW=normalize(UnityWorldSpaceViewDir(i.posW));
    float3 h=max(0.0,L+viewDirW);
    float HdotN=saturate(dot(h,norW));
                
    float3 specu=pow(HdotN,_smoothness)*_LightColor0.xyz*_specuIntensity;

                                BliPhong                                                        Phong

2、ACESFilm tone-mapping:

显示器显示范围为0-1,为LDR,而计算机渲染可以是任意数值。HDR→LDR过程造成失真,丢失许多细节。(HDR:高动态范围)

解决这个问题:HDR→LDR(tone-mapping)

tone-mapping(个人经验),pow(basecolor(diffuse2d),2.2)转换至线性空间,pow(tonecolor,1.0/2.2)转换至伽马空间。

 

                         ACESFilm                                                                  not

 全局环境光开启泛光?解决:globalAmbient×baseColor;

3、视差贴图(原理还是不熟悉):

置换贴图,模型表面曲面细分,高密度顶点,读取高度图,顶点偏移。深度图,其实是高度图的反相,白色代表凹陷

for (int i = 0; i < 10; i++)
{
	half height = tex2D(_ParallaxMap, uv_parallax);
	uv_parallax = uv_parallax+ (1.0 - height) * view_tangentspace.xy * _Parallax * 0.01f;
}

 

                         开启                                                                        未开启

4、shadowmap

  • shadowmap
  • screen space shadowmap
  • 联级阴影(csm) cascaded shadowmapping:shadowmap基础上,视锥体分级

shadowmap就是一张深度图,

shadowmap实现:光源相机渲染深度信息,物体转换至光源相机空间,跟shadowmap里的深度值作比较,若片元深度值>深度值(shadowmap),光面。反之阴影面。

联级阴影以及屏幕空间下的阴影还需深入学习!

注意:光位置的w分量可以判断是否为平行光,0为平行光。1为点光。

5、间接光照

5.1基本概念

环境光≈间接光

三种技术实现:

  • LightMap(预先烘焙好)
  • 反射探针:捕抓环境信息→环境贴图,模拟漫反射
  • 光照探针:预先收集环境和光源的漫反射信息,模拟漫反射;

实现原理:

  • 环境贴图
  • IBL基于图像的照明
  • SH球谐光照

CubeMap采样全景图实现:

 反射探针和CubeMap实现:

fixed4 frag (v2f i) : SV_Target
{
	// sample the texture
	float3 viewDir=UnityWorldSpaceViewDir(i.posW);
	float3 normalW=normalize(i.normalW);
	float3 ref=reflect(-viewDir,normalW);
	//反射探针
	float4 environColor=UNITY_SAMPLE_TEXCUBE(unity_SpecCube0,ref);
	//为了移动端也可以获取到HDR的信息
	float3 hdrColor=DecodeHDR(environColor,unity_SpecCube0_HDR);
	//fixed4 col = texCUBE(_CubeMap, ref);//CubeMap
				
	return float4(hdrColor,1.0);
}

6、IBL 基于图像的照明

ibl ,一个设置一行代码。

为什么是mipmap?

pow对比度,直接相乘亮度,lerp整体粗糙度,线性粗糙度

ibl镜面反射和漫反射

使用IBL要三线性过滤

IBL实现一般不需要大尺寸(压缩压缩再压缩,,,,)

IBL间接镜面反射光:

fixed4 frag (v2f i) : SV_Target
{
	// 取rougness,算出mipmap的level
	float roughness=tex2D(_RoughtMap,i.uv).r;
	roughness=pow(roughness,_Contrastion)*_Brighteness;
	roughness=lerp(_RoughnessMin,_RoughnessMax,roughness);
	roughness = roughness * (1.7 - 0.7 * roughness);
	float level=roughness*6.0;

    //取法线,算反射
	float3 normaldata= UnpackNormal(tex2D(_NormalMap,i.uv));
	float3 normalW=normalize(i.normalW);
	float3 tangent=normalize(i.tangent);
	float3 binormal=normalize(i.binormal); 
	float3x3 TBN=float3x3(tangent,binormal,normalW);
	float3 normal=normalize(mul(normaldata,TBN));
	float3 viewDir=UnityWorldSpaceViewDir(i.posW);
	float3 reflectDir=reflect(-viewDir,normal);

    //ao
	float aoMap=tex2D(_AOMap,i.uv).r;
	float ao= lerp(1.0,aoMap,_AOAdjust);

    //cubemap的ibl实现
	float4  cubeMap=texCUBElod(_CubeMap,float4(reflectDir,level));
	float3  envir=DecodeHDR(cubeMap,_CubeMap_HDR).rgb;

    //cubemap的probe实现
    //half4 color_cubemap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectDir, level);
	//half3 envir = DecodeHDR(color_cubemap, unity_SpecCube0_HDR);//确保在移动端能拿到HDR信息

    //最终颜色
	float3  col=envir*_Tint.rgb*_Tint.rgb*ao* _Expose;

	//tonemapping
	col=pow(col,2.2);
	col=ACES_Tonemapping(col);
	col=pow(col,1/2.2);

	return float4(col,1.0);
}

IBL间接漫反射光: 

float4 uv_ibl = float4(normal_dir, mip_level);

half4 color_cubemap = texCUBElod(_CubeMap, uv_ibl);
half3 env_color = DecodeHDR(color_cubemap, _CubeMap_HDR);//确保在移动端能拿到HDR信息

7、球谐光照(SH)

sh球谐光照内置实现(light probe的probe)

在forwardbase实现;

造轮子实现:

half4 frag (v2f i) : SV_Target
{
	half3 normal_dir = normalize(i.normal_world);
	half3 normaldata = UnpackNormal(tex2D(_NormalMap,i.uv));
	normaldata.xy = normaldata.xy* _NormalIntensity;
	half3 tangent_dir = normalize(i.tangent_world);
	half3 binormal_dir = normalize(i.binormal_world);
	normal_dir = normalize(tangent_dir * normaldata.x
		+ binormal_dir * normaldata.y + normal_dir * normaldata.z);

	half ao = tex2D(_AOMap, i.uv).r;
	ao = lerp(1.0,ao, _AOAdjust);

	float4 normalForSH = float4(normal_dir, 1.0);
	//SHEvalLinearL0L1
	half3 x;
	x.r = dot(custom_SHAr, normalForSH);
	x.g = dot(custom_SHAg, normalForSH);
	x.b = dot(custom_SHAb, normalForSH);

	//SHEvalLinearL2
	half3 x1, x2;
	// 4 of the quadratic (L2) polynomials
	half4 vB = normalForSH.xyzz * normalForSH.yzzx;
	x1.r = dot(custom_SHBr, vB);
	x1.g = dot(custom_SHBg, vB);
	x1.b = dot(custom_SHBb, vB);

	// Final (5th) quadratic (L2) polynomial
	half vC = normalForSH.x*normalForSH.x - normalForSH.y*normalForSH.y;
	x2 = custom_SHC.rgb * vC;

	float3 sh = max(float3(0.0, 0.0, 0.0), (x + x1 + x2));
	sh = pow(sh, 1.0 / 2.2);

	half3 env_color = sh;
	half3 final_color = env_color * ao * _Tint.rgb * _Expose;
	
	return float4(final_color,1.0);
}

unity内置实现:

half4 frag (v2f i) : SV_Target
{
	half3 normal_dir = normalize(i.normal_world);
	half3 normaldata = UnpackNormal(tex2D(_NormalMap,i.uv));
	normaldata.xy = normaldata.xy* _NormalIntensity;
	half3 tangent_dir = normalize(i.tangent_world);
	half3 binormal_dir = normalize(i.binormal_world);
	normal_dir = normalize(tangent_dir * normaldata.x
		+ binormal_dir * normaldata.y + normal_dir * normaldata.z);

	half ao = tex2D(_AOMap, i.uv).r;
	ao = lerp(1.0,ao, _AOAdjust);


	half3 env_color = ShadeSH9(float4(normal_dir,1.0));
	half3 final_color = env_color * ao * _Tint.rgb * _Expose;
	
	return float4(final_color,1.0);
}

unity全局间接光:

  • lighting-environment lighting        漫反射
  • lighting-environment refection      镜面反射

lighting-mixed lighting-baked global illumination        烘焙多光源

8、玉石模拟

玉石光线(穿透感)+光滑质感(环境反射)

组成:漫反射+透射+环境光;

漫反射=phong漫反射+addcolor+竖直方向AO;

透射计算:VdotB(B为L扭曲而成),结合对比度、亮度,乘以模型厚度、basecolor(光源颜色影响世界,所以独立一个光源颜色)和平行光颜色;

环境光:cubemap结合菲涅尔;

forbase:

float4 frag(v2f i) : COLOR
{
	//info
	float3 diffuse_color = _DiffuseColor;
	float3 normalDir = normalize(i.normalDir);
	float3 viewDir = normalize(_WorldSpaceCameraPos - i.posWorld.xyz);
	float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

	//diffuse
	float diff_term = max(0.0, dot(normalDir, lightDir));
	float3 diffuselight_color = diff_term * diffuse_color * _LightColor0.rgb;

    //竖直方向的AO,上亮下暗
	float sky_sphere = (dot(normalDir,float3(0,1,0)) + 1.0) * 0.5;
	float3 sky_light = sky_sphere * diffuse_color;
	float3 final_diffuse = diffuselight_color + sky_light * _Opacity + _AddColor.xyz;

	//trans light
	float3 back_dir = -normalize(lightDir + normalDir * _BasePassDistortion);
	float VdotB = max(0.0, dot(viewDir, back_dir));
	float backlight_term = max(0.0,pow(VdotB, _BasePassPower)) * _BasePassScale;
	float thickness = 1.0 - tex2D(_ThicknessMap, i.uv).r;
	float3 backlight = backlight_term * thickness *
		_LightColor0.xyz * _BasePassColor.xyz;

	//ENV
	float3 reflectDir = reflect(-viewDir,normalDir);

	half theta = _EnvRotate * UNITY_PI / 180.0f;
	float2x2 m_rot = float2x2(cos(theta), -sin(theta), sin(theta),cos(theta));
	float2 v_rot = mul(m_rot, reflectDir.xz);
	reflectDir = half3(v_rot.x, reflectDir.y, v_rot.y);

	float4 cubemap_color = texCUBE(_EnvMap,reflectDir);
	half3 env_color = DecodeHDR(cubemap_color, _EnvMap_HDR);

	float fresnel = 1.0 - saturate(dot(normalDir, viewDir));
	fresnel = smoothstep(_FresnelMin, _FresnelMax, fresnel);

	float3 final_env = env_color * _EnvIntensity * fresnel;
	//combine
	float3 combined_color = final_diffuse + final_env + backlight;
	float3 final_color = combined_color;
	return float4(final_color,1.0);
}

forwardadd:

float4 frag(v2f i) : COLOR
{
	float3 diffuse_color = _DiffuseColor * _DiffuseColor;

	float3 normalDir = normalize(i.normalDir); 
	float3 viewDir = normalize(_WorldSpaceCameraPos - i.posWorld.xyz);
	float NdotV = saturate(dot(normalDir,viewDir));
	//light info
	float3 lightDir = normalize(lerp(_WorldSpaceLightPos0.xyz, _WorldSpaceLightPos0.xyz - i.posWorld.xyz,_WorldSpaceLightPos0.w));
	float attenuation = LIGHT_ATTENUATION(i);
	//trans light
	float3 back_dir = -normalize(lightDir + normalDir * _AddPassDistortion);
	float VdotB = max(0.0,dot(viewDir, back_dir));
	float backlight_term = max(0.0, pow(VdotB, _AddPassPower)) * _AddPassScale;
	float thickness = 1.0 - tex2D(_ThicknessMap, i.uv).r;
	float3 backlight = backlight_term * thickness *
		_LightColor0.xyz * _AddPassColor.xyz;
	//combine
	float3 final_color = backlight;
	final_color = sqrt(final_color);
	return float4(final_color,1.0);
}

9、移动端角色渲染

分析与问题:

  • ue和unity的单位区别(米、厘米);
  • x轴向的区别;
  • 模型和骨骼动画的大量到处;
  • Mc纹理,rg通道粗糙度、金属度;
  • 角色脚部抖动,曲线优化和压缩问题;

细节:

  • 皮肤区域高光弱,金属区域高光强,albedo贴图的使用原理其一
  • 皮肤区域漫反射强,金属区域漫反射弱,albedo贴图的使用原理其二
  • 粗糙度做文章,越粗糙,光滑度越低
  • 阴影不在间接光中计算,仅在直接光中,因为间接光的作用在于提亮暗部
  • 皮肤正常sh,其他乘以0.5sh

疑问:

  1. 间接光的漫反射为什么要限定为0.5到1的范围内?

        解答:使用了半兰伯特模型,提亮暗部

    2. 153/154的操作不是很能理解,间接光都要乘以这个?

        解答:shininess x smoothness ≈ a(1-a)x+a²,a为smoothness,但是还是线性关系

    3.  皮肤柔光的插值操作

公式总结:

  • 直接光漫反射:diffterm x basecolor x atten x lightcolor
  • 直接光镜面反射:specterm x speccolor x atten x lightcolor
  • 间接光漫反射:sh x basecolor x halflambert
  • 间接光镜面反射:ibl x expose x speccolor x halflambert

头发渲染:kk的各向异性高光渲染

技术原理:normal实现的高光仅仅是一点,我们需要在头发的横截面都有高光

公式:T、L、V三个成关系,LV成半角关系H,求sin(T,H);

细节:

  • T根据normal进行偏移,高光即可实现偏移;
  • 噪声图扰动各向异性方向,范围由0到1缩放到-1到1;
  • N x noise
  • half_lambert/NdotL ,防止背光区域也有高光

疑问:

1. 为什么各向异性高光只亮一处?

因为sin(T,H)的夹角为90°时才为1

2. 为什么成光盘一样的扇形?

因为顺着边缘变化,中间的偏移更大,两边渐变,越远渐变范围越大;而中心范围小是因为没有偏移

细节语法:

1. 宏定义:检查器隐藏

2. 宏开关:多重编译和shader特性,增加一个,代码量两倍

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值