UnityShader入门精要学习笔记(十四):立方体纹理

时间:2022-11-04 10:18:25

一.立方体纹理
在图形学中,立方体纹理(CubeMap)是环境映射(Environment Mapping)的一种实现方法。环境映射可以模拟物体周围IDE环境,而使用了环境映射的物体可以看起来像镀了层金属一样反射出周围的环境。

二.反射

使用了反射效果的物体通常看起来就像镀了一层金属。想要模拟反射效果很简单,我们只需要通过入射光线的方向和表面法线的方向来计算反射方向,再利用反射方向对立方体纹理进行采样即可。

代码实践

Shader "Custom/Edu/Reflection" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_ReflectColor("ReflectColor",Color) = (1,1,1,1)
_ReflectAmount("ReflectAmount",Range(0,1)) = 0.5
_Cubemap("Reflection Cube Map",Cube) = "_Skybox"{}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue" = "Geometry" }
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

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

fixed4 _Color;
fixed4 _ReflectColor;
float _ReflectAmount;

//注意这里叫samplerCUBE
samplerCUBE _Cubemap;

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

struct v2f
{
float4 pos:SV_POSITION;
fixed3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
fixed3 worldViewDir:TEXCOORD2;
fixed3 worldRefl:TEXCOORD3;

//定义阴影纹理存储在哪个纹理寄存器
SHADOW_COORDS(4)
};

v2f vert(a2v v)
{
v2f o;

o.pos = mul(UNITY_MATRIX_MVP,v.vertex);

o.worldPos = UnityObjectToWorldNormal(v.normal);

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos) ;

//物体反射到摄像机中的光线方向,可以由光路可逆的原则来求反来取得。
//也就是说,我们可以计算视角方向关于顶点法线的反射方向来求得入射光线的方向。
o.worldRefl = reflect(-normalize(o.worldViewDir),normalize(o.worldNormal));

//将阴影坐标传递给片元着色器
TRANSFER_SHADOW(o);

return o;
}

fixed4 frag(v2f i):SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
//fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * saturate(dot(worldNormal,worldLightDir));

fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
//flaot3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));

//注意这里的书写texCUBE
fixed3 reflection = texCUBE(_Cubemap,i.worldRefl).rgb * _ReflectColor.rgb;

//计算光照衰减系数
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);

//fixed3 halfDir = normalize(worldLightDir + worldViewDir);
//fixed3 specular = _LightColor0.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss);

return fixed4(ambient + lerp(diffuse,reflection,_ReflectAmount)*atten,1.0);

}

ENDCG
}
}
FallBack "Reflective/VertexLit"
}

效果图

UnityShader入门精要学习笔记(十四):立方体纹理

三.折射

原理图

UnityShader入门精要学习笔记(十四):立方体纹理

介绍一下将要用到的CG函数refract

    函数:refract(v,n,r)
参数:v表示归一化的入射方向,通常可对viewDir求反获得
n表示归一化的入射表面的法线方向
r表示两材质之间的折射率
返回值:光线的折射方向

代码实践

Shader "Custom/Edu/Refract" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_RefractColor("Refraction Color",Color) = (1,1,1,1)
_RefractAmount("Refraction Amount",Range(0,1)) = 0.5
_RefractRatio("Refraction Ratio",Range(0.1,1)) = 0.5
//默认的_Skybox是白色的天空盒
_CubeMap("Refraction CubeMap",Cube) = "_Skybox"{}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue" = "Geometry"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"

fixed4 _Color;
fixed4 _RefractColor;
fixed _RefractAmount;
fixed _RefractRatio;
samplerCUBE _CubeMap;

struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
fixed3 worldNormal:TEXCOORD0;
fixed3 refrDir:TEXCOORD1;
float3 worldPos:TEXCOORD2;
fixed3 worldViewDir:TEXCOORD3;
SHADOW_COORDS(4)
};

v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.worldPos = mul(_Object2World,v.vertex);
o.worldNormal = normalize( UnityObjectToWorldNormal(v.normal) );
o.worldViewDir = normalize( UnityWorldSpaceViewDir(o.worldPos) );
o.refrDir = refract(-o.worldViewDir,o.worldNormal,_RefractRatio);
return o;
}

fixed4 frag(v2f i):SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldLightDir = UnityWorldSpaceViewDir(i.worldPos);
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(i.worldNormal,worldLightDir));

fixed3 refraction = texCUBE(_CubeMap,i.refrDir).rgb * _RefractColor.rgb;
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);

fixed3 color = ambient + lerp(diffuse,refraction,_RefractAmount) * atten;

return fixed4(color,1.0);
}

ENDCG
}
}
FallBack "Diffuse"
}

效果图

UnityShader入门精要学习笔记(十四):立方体纹理

UnityShader入门精要学习笔记(十四):立方体纹理

四.菲涅尔反射

在实时渲染中,我们经常会用到菲涅尔反射来根据视角方向控制反射成都。

    通俗地讲,菲涅尔反射描述了一种光学现象,即当光线照射到物体表面上是,一部分发生了反射,一部分进入了物体内部,发生折射或者散射。
被反射的光和入射光之间存在一定的比率关系,这个比率关系可以通过菲涅尔等式进行计算。

生活中的实例,站在水塘边,近处可以看到池底,远处只能看到周围景物或者天空的倒影。

UnityShader入门精要学习笔记(十四):立方体纹理

菲涅尔近似等式

Fschlick = F0 + (1-F0)(1-v·n)5

其中 F0 是一个反射系数,用于控制菲涅尔反射的强度,v是视角方向,是表面法线

在血多车漆、水面、木材等材质的渲染中,我们经常会使用菲涅尔反射来模拟更加真实的反射效果。

在代码中的实际使用就是在Lerp函数混合diffuse和reflection时,将fresnel得到的结果作为插值变量

代码实践

Shader "Custom/Edu/Fresnel" {
Properties {
_Color ("DiffuseColor", Color) = (1,1,1,1)
_FresnelScale("Fresnel Scale",Range(0,1)) = 0.5
_Cubemap("Reflection Cubemap",CUBE) = "_skybox"{}
_FresnelPow("Fresnel Power",range(1,5)) = 3.0
}
SubShader{
Tags{"RenderType"="Opaque" "Queue" = "Geometry"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "unityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"

fixed4 _Color;
fixed _FresnelScale;
samplerCUBE _Cubemap;
half _FresnelPow;

struct a2v
{
float4 vertex:POSITION;
fixed3 normal:NORMAL;
};

struct v2f
{
float4 pos:SV_POSITION;
float3 worldPos:TEXCOORD0;
fixed3 worldNormal:TEXCOORD1;
fixed3 worldViewDir:TEXCOORD2;
fixed3 worldReflDir:TEXCOORD3;
SHADOW_COORDS(4)
};

v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.worldPos = mul(_Object2World,v.vertex);
o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
o.worldViewDir = normalize(UnityWorldSpaceViewDir(o.worldPos));
o.worldReflDir = reflect(-o.worldViewDir,o.worldNormal);

TRANSFER_SHADOW(o);

return o;
}

fixed4 frag(v2f i):SV_Target
{
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 reflection = texCUBE(_Cubemap,i.worldReflDir).rgb;

fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(i.worldNormal,worldLightDir));

fixed fresnel = _FresnelScale + (1-_FresnelScale)*pow(1-dot(i.worldNormal,i.worldViewDir),_FresnelPow);

//潜意识中的错误写法
//UNITY_SHADOW_ATTENUATION(atten,i,i.worldPos);
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);

fixed3 color = ambient + lerp(diffuse,reflection,fresnel) * atten;

return fixed4(color,1.0);
}

ENDCG
}
}
FallBack "Diffuse"
}

效果图:

UnityShader入门精要学习笔记(十四):立方体纹理

UnityShader入门精要学习笔记(十四):立方体纹理