您的位置:首页 > 移动开发 > Unity3D

Unity——ShaderLab实现玻璃和镜子效果

2022-01-05 14:05 751 查看

在这一篇中会实现会介绍折射和反射,以及菲尼尔反射;并且实现镜子和玻璃效果;

这里和之前不同的地方在于取样的是一张CubeMap;

demo里的cubemap使用的一样,相机所在位置拍出来的周围环境图;

生成CubeMap的工具脚本

public class RenderCubemapWizard : ScriptableWizard {

public Transform renderFromPosition;
public Cubemap cubemap;

void OnWizardUpdate () {
helpString = "Select transform to render from and cubemap to render into";
isValid = (renderFromPosition != null) && (cubemap != null);
}

void OnWizardCreate () {
// create temporary camera for rendering
GameObject go = new GameObject( "CubemapCamera");
go.AddComponent<Camera>();
// place it on the object
go.transform.position = renderFromPosition.position;
// render into cubemap
go.GetComponent<Camera>().RenderToCubemap(cubemap);

// destroy temporary camera
DestroyImmediate( go );
}

[MenuItem("GameObject/Render into Cubemap")]
static void RenderCubemap () {
ScriptableWizard.DisplayWizard<RenderCubemapWizard>(
"Render cubemap", "Render!");
}
}

1.反射

用反射方向在CubeMap上取样,_ReflectAmount控制反射程度,_ReflectColor反射颜色;

v2f vert (appdata v){
//计算反射向量
o.worldReflect = reflect(-o.worldViewDir,o.worldNormal);
...
}

fixed4 frag (v2f i) : SV_Target{
//根据反射向量从cubemap纹理上取样
fixed3 reflection = texCUBE(_Cubemap,i.worldReflect).rgb * _ReflectColor.rgb;

//混合反射和漫反射
return fixed4(ambient + lerp(diffuse,reflection,_ReflectAmount)*atten, 1.0);
}

2.折射

和反射几乎相同,将反射改成折射,计算公式改成折射计算公式;

v2f vert (appdata v){
//计算反射向量
o.worldRefract = refract(-normalize(o.worldViewDir),normalize(o.worldNormal),_RefractRatio);
...
}

fixed4 frag (v2f i) : SV_Target{
//根据反射向量从cubemap纹理上取样
fixed3 refraction = texCUBE(_Cubemap,i.worldRefract).rgb * _RefractColor.rgb;

//混合反射和漫反射
return fixed4(ambient + lerp(diffuse,refraction,_RefractAmount)*atten, 1.0);
}

成像是倒的;透过茶壶可以看到对面;

3.菲尼尔

反射光的强度与视线方向和法线方向的夹角有关,夹角越大反射光越强;最高90度,也就是边缘光最强;

Schlick菲尼尔公式:Fschlick(v,n) = F0 + (1-F0)(1- dot(v,n)) ^ 5;F0控制菲尼尔强度;

fixed4 frag (v2f i) : SV_Target{
...
//Schlick Fresnel——边缘光
fixed3 reflection = texCUBE(_Cubemap,i.worldRefl).rgb;
fixed3 fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir,worldNormal), 5);

//菲尼尔系数控制反射强度
return fixed4(ambient + lerp(diffuse,reflection,saturate(fresnel)) * atten, 1.0);
}

4.玻璃效果

通过GrabPass{"_RefractionTex"} 抓取当前屏幕内容渲染到_RefractionTex贴图上

RefractionTex贴图用来取样折射纹理;_Distortion参数模拟法线扰动的程度;

GrabPass{"_RefractionTex"}

...
//GrabPass纹理
sampler2D _RefractionTex;
//纹素大小
float4 _RefractionTex_TexelSize;

fixed4 frag (v2f i) : SV_Target
{
//法线偏移扰动-模拟折射
fixed3 bump = UnpackNormal(tex2D(_BumpMap,i.uv.zw));
float2 offset = bump.xy*_Distortion*_RefractionTex_TexelSize.xy;

//折射计算-屏幕坐标偏移后透视除法取样折射纹理
i.screenPos.xy = offset + i.screenPos.xy;
fixed3 refractColor = tex2D(_RefractionTex,i.screenPos.xy/i.screenPos.w).rgb;

//矩阵计算世界法线
bump = normalize(half3(dot(i.TtoW0.xyz,bump),dot(i.TtoW1.xyz,bump),dot(i.TtoW2.xyz,bump)));

//反射计算
fixed3 reflectDir = reflect(-worldViewDir,bump);
fixed4 texColor = tex2D(_MainTex,i.uv.xy);
fixed3 reflectColor = texCUBE(_Cubemap,reflectDir).rgb * texColor.rgb;

//混合反射和折射_RefractAmount
return fixed4(reflectColor*(1-_RefractAmount)+refractColor*_RefractAmount, 1.0);
}

5.镜子

tex2Dproj(_ReflectionTex,UNITY_PROJ_COORD(i.refl));

UNITY_PROJ_COORD:given a 4-component vector, return a texture coordinate suitable for projected texture reads. On most platforms this returns the given value directly.

传入Vector4,返回一张用来投影取样的纹理,大部分平台直接返回给定值;

镜子直接传入屏幕顶点坐标获得投影纹理,再通过投影取样获得颜色,和最终结果混合;

但是上面效果和局限性都比较大,所以找了个大佬写的镜子效果;

使用相机和RenderTexture,底层原理差不多,效果要好了很多;

Unity镜子效果制作教程

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: