本篇主要用于记录自己的实战操作,以及一些碎碎念(观后感),如果有什么好想法或者本篇出现什么错误,请多指教~
本篇的内容参考视频:庄懂的技术美术入门课(美术向)
使用软件:Unity 2019 3.6f1 ,ShaderForge
本篇内容主要包括:拆解菲涅尔,Matcap,Cubemap,oldSchoolPro;
一.菲涅尔
1.菲涅尔现象:如果你站在湖边,低头看脚下的水,你会发现水是透明的,反射不是特别强烈;如果你看远处的湖面,你会发现水并不是透明的,但反射非常强烈。这就是“菲涅尔效应”。自然界中的都有菲涅尔效应,金属也是,只不过影响很小罢了(此为修正具体参考PBR的讲解,原文为在真实世界中,除了金属之外,其它物质均有不同程度的“菲涅尔效应”。 )
2.连连看拆解菲涅尔
用法线向量点乘视向量,再用1减去后,使用Power控制效果得到最后的菲涅尔;
二.Matcap
1.Matcap
1.Matcap被称为材质捕获,效果如上图所示,具体的了解可以参考这篇,我们一般使用Matcap可以做环境的镜面反射,商城界面,展示界面之类的;
2.Matcap连连看
如上图,比起上篇使用code节点自己编写通过TBN矩阵转换法线贴图中的法线信息,这里使用Transform节点对法线信息进行转换,之后对其取R和G通道,分别对应在屏幕空间下的从左到右,从上到下的位置关系,在Remap之后该关系如下:
法线朝向 | 取值 |
---|---|
左 | R=1 |
右 | R=0 |
上 | G=1 |
下 | G=0 |
指向屏幕前的自己 | R=0.5,G=0.5 |
R和G通道取值范围刚好是构成一个圆,所以Matcap一般就是图片里一个圆;
3.Matcap代码
Shader "Unlit/co9-matcap-VS"
{
Properties
{
_NormalMap ("法线贴图",2D) = "bump" {}
_Matcap("Matcap",2D)="white"{}
_fresnelexp("菲涅尔次幂",range(0,5))=2
_Envcontrol("环境光强度",range(0,1))=1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _NormalMap;
uniform sampler2D _Matcap;
uniform float _fresnelexp;
uniform float _Envcontrol;
struct appdata
{
float4 vertex : POSITION;
float3 normal :NORMAL;
float4 tangent : TANGENT;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct v2f
{
float2 uv0 : TEXCOORD0;
float3 posWS : TEXCOORD1;
float4 pos : SV_POSITION;
float3 nDirWS : TEXCOORD2;
float3 tDirWS : TEXCOORD3;
float3 bDirWS : TEXCOORD4;
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.posWS = mul(unity_ObjectToWorld,v.vertex);
o.uv0 = v.uv0;
o.nDirWS=UnityObjectToWorldNormal(v.normal);
o.tDirWS = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)));
o.bDirWS = normalize(cross(o.nDirWS,o.tDirWS)*v.tangent.w);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 nDirTS=UnpackNormal(tex2D(_NormalMap,i.uv0));
float3x3 TBN=float3x3(i.tDirWS,i.bDirWS,i.nDirWS);
float3 nDirWS=normalize(mul(nDirTS,TBN));
float3 nDirVS=mul(UNITY_MATRIX_V,float4(nDirWS,0.0));
float3 vDirWS=normalize(_WorldSpaceCameraPos.xyz-i.posWS);
float2 matcapUV=nDirVS.rg*0.5+0.5;
float ndotv=dot(nDirWS,vDirWS);
float3 matcap=tex2D(_Matcap,matcapUV);
float fresnel=pow(1-ndotv,_fresnelexp);
float3 envSpecLighting=matcap*fresnel*_Envcontrol;
return float4(envSpecLighting,1.0);
}
ENDCG
}
}
}
三.CubeMap
1.早期存在六张图里,现在一般存储在一张图里,如图中的彩图那般处理后得到;
2.连连看,图里的Mipmap就是纹理里讲的那个,我们这里是利用Mipmap表现不同粗糙度的反射;
3.全景图处理:
- 首先我们在PS里打开一张exr格式的全景图,选择作为Alpha通道打开;
- 然后我们把图像修改成符合项目标准的方形;
- 由于原图还是HDR高动态光照渲染的图,我们将其转换为低动态的将原本不在0-1的部分映射到0-1;
- 然后以tga格式导入到Unity中,Textureshape修改为cube,Mapping那里默认auto识别会出错要手动改为Latitude,convolution type默认None手动改为Specular,这里跟Mipmap挂钩,在None的情况下,会突显一个个格子,不如Specular好使,Fixup edge seams(修正接缝)勾选;
4.Cubemap连连看
5.cubeMap代码
Shader "Unlit/co9-cubemap-VS"
{
Properties
{
_NormalMap ("法线贴图",2D) = "bump" {}
_CubeMap ("CubeMap",Cube)="_Skybox"{}
_Mipmap ("Mipmap",range(0,7))=0
_fresnelexp("菲涅尔次幂",range(0,5))=0.2
_Envcontrol("环境光强度",range(0,5))=1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _NormalMap;
uniform samplerCUBE _CubeMap;
uniform float _Mipmap;
uniform float _fresnelexp;
uniform float _Envcontrol;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 nDirWS:TEXCOORD1;
float3 bDirWS:TEXCOORD2;
float3 tDirWS:TEXCOORD3;
float4 pos : SV_POSITION;
float3 posWS:TEXCOORD4;
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.posWS=mul(unity_ObjectToWorld,v.vertex);
o.uv = v.uv;
o.nDirWS = UnityObjectToWorldNormal(v.normal);
o.tDirWS = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)));
o.bDirWS = normalize(cross(o.nDirWS,o.tDirWS)*v.tangent.w);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 vDirWS=normalize(_WorldSpaceCameraPos-i.posWS);
float3 nDirTS=UnpackNormal(tex2D(_NormalMap,i.uv));
float3x3 TBN =float3x3(i.tDirWS,i.bDirWS,i.nDirWS);
float3 nDirWS=normalize(mul(nDirTS,TBN));
float3 vrDir=reflect(-vDirWS,nDirWS);
float ndotv=dot(nDirWS,vDirWS);
float fresnel=pow(1-ndotv,_fresnelexp);
float3 cubemap=texCUBElod(_CubeMap,float4(vrDir,_Mipmap));
float3 final=cubemap*fresnel*_Envcontrol;
return float4(final,1.0);
}
ENDCG
}
}
}
四.OldSchoolPro
1.SF连连看
看着复杂其实就是将之前学过的东西都耦合在一起
2.加强版代码
分类管理可调参数,使用lerp对高光和Cubemap进行灵活柔和处理;
Shader "Unlit/co9-oldschoolpro+VS"
{
Properties
{
[Header(Texture)]
_MainTex ("RGB:基础颜色,A:环境遮罩", 2D) = "white" {}//这里将AO贴图与颜色贴图整合到了一张图里
_NormalTex ("RGB:法线贴图",2D) = "bump" {}
_SpecTex ("RGB:高光颜色,A:高光次幂",2D) = "gray" {}
_EmitTex ("RGB:自发光贴图",2d) = "black" {}
_Cubemap ("RGB:环境贴图Cubemap",Cube) = "_Skybox" {}
[Header(Diffuse)]
_BaseColor ("基础颜色",color) = (1.0,1.0,1.0,1.0)
_EnvUpColor ("环境天顶颜色",color) = (1.0,1.0,1.0,1.0)
_EnvMidColor ("环境侧部颜色",color) = (1.0,1.0,1.0,1.0)
_EnvDowColor ("环境底部颜色",color) = (1.0,1.0,1.0,1.0)
_EnvDiffuse ("环境漫反射强度",range(0,1)) = 0.2
[Header(Specular)]
_SpecularPow ("高光次幂",range(1,90)) = 25
_EnvSpecInt ("高光强度",range(0,5)) = 1
_FresenlPow ("菲涅尔次幂",range(0,10)) = 3
_CubemapMip ("环境球Mip",range(0,7)) = 0
[Header(Emission)]
_Emitint ("自发光强度",range(1,10)) = 5
}
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 "Lighting.cginc"
#include "AutoLight.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _NormalTex;
uniform sampler2D _SpecTex;
uniform sampler2D _EmitTex;
uniform samplerCUBE _Cubemap;
uniform float3 _BaseColor;
uniform float3 _EnvUpColor;
uniform float3 _EnvMidColor;
uniform float3 _EnvDowColor;
uniform float3 _EnvDiffuse;
uniform float _SpecularPow;
uniform float _EnvSpecInt;
uniform float _FresenlPow;
uniform float _CubemapMip;
uniform float _Emitint;
struct appdata
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float4 tangent:TANGENT;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 posWS : TEXCOORD1;
float3 nDirWS : TEXCOORD2;
float3 bDirWS : TEXCOORD3;
float3 tDirWS : TEXCOORD4;
LIGHTING_COORDS(5,6)
};
v2f vert (appdata v)
{
v2f o;
o.uv = v.uv;
o.pos = UnityObjectToClipPos(v.vertex);
o.posWS = mul(unity_ObjectToWorld,v.vertex);
o.nDirWS = UnityObjectToWorldNormal(v.normal);
o.tDirWS = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,1.0)));
o.bDirWS = normalize(cross(o.nDirWS,o.tDirWS));
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 nDirTS = UnpackNormal(tex2D(_NormalTex,i.uv));
float3x3 TBN = float3x3(i.tDirWS,i.bDirWS,i.nDirWS);
float3 nDirWS = normalize(mul(nDirTS,TBN));
float3 lDir = _WorldSpaceLightPos0;
float3 vDir = normalize(_WorldSpaceCameraPos-i.posWS.xyz);
float3 vrDir = reflect(-vDir,nDirWS);
float3 lrDir = reflect(-lDir,nDirWS);
float3 ndotl = dot(nDirWS,lDir); //兰伯特
float3 ndotv = dot(nDirWS,vDir); //菲涅尔
float3 vdotlr = dot(vDir,lrDir); //Phong
float4 var_MainTex = tex2D(_MainTex,i.uv);
float4 var_SpecTex = tex2D(_SpecTex,i.uv);
float3 var_EmitTex = tex2D(_EmitTex,i.uv);
float3 var_Cubemap = texCUBElod(_Cubemap,float4(vrDir,lerp(_CubemapMip,1.0,var_SpecTex.a)));//lerp用来表现越光滑反射越清晰
float Lambert = max(ndotl,0);
float spect = lerp(1,_SpecularPow,var_SpecTex.a);//用lerp做插值
float Phong = pow(max(0,vdotlr),spect);
float shadow = LIGHT_ATTENUATION(i);
float3 dirtLight = (Lambert*_BaseColor*var_MainTex.rgb + Phong*var_SpecTex.rgb)*shadow*_LightColor0;
float topmask = max(nDirWS.g,0.0);
float dowmask = max(-nDirWS.g,0.0);
float midmask = 1.0-topmask-dowmask;
float3 col3 = topmask*_EnvUpColor+midmask*_EnvMidColor+dowmask*_EnvDowColor;
float fresnel = pow(max(1-ndotv,0.0),_FresenlPow);
float occluision = var_MainTex.a;
float Envlighting = (_BaseColor*var_MainTex.rgb*col3*_EnvDiffuse+var_Cubemap*fresnel*_EnvSpecInt)*occluision;
float3 emission = var_EmitTex*_Emitint;
float3 final = dirtLight+Envlighting+emission;
return float4(final,1.0);
}
ENDCG
}
}
}
效果图 (老师的包里没自发光贴图,PS里随手画的,确实不好看)