文章目录
- Fresnel
- shader forge实现
- UE4蓝图实现
- Matcap
- shader forge实现
- UE4蓝图实现
- CubeMap
- SD和PS制作所需的贴图
- shader forge实现
- unity代码实现
- UE4蓝图实现(未实现)
Fresnel
shader forge实现
- 个人理解是,使用观察方向和法向方向点乘,那就相当于我们的视线看到哪里,哪里就是亮的,相当于头顶探照灯了。
- 但是菲尼尔效果则是观察方向和法线方向离得越近,折射或者反射效果越弱;而如果观察方向和法线方向越接近90°时,反射的效果就越强。所以我们采用onemiuns(用1减去观察方向和法线方向的点乘),就能取得我们想要的效果。
- 最后再使用power函数来增强这个菲涅尔的强度。
UE4蓝图实现
然后是ue4中的fresnel效果的实现,跟shader forge的节点差不多:
- ue4中对应于shader forge中使用的法线方向的节点是PixelNormalWS,而不是VertexNormalWS这点需要注意。
- 和unity中不同的时我们的观察方向需要通过摄像机方向取反来得到,因为shader forge的观察方向是从着色点为起点发射到相机的位置。这与ue4中的相机方向刚好相反。
Matcap
具体请参考毛星云大佬的blog
不像一般的Shader,需要提供光照,需要在Shader代码中进行漫长的演算,基于MatCap思想的Shader相当于MatCap贴图就把光照结果应该是怎样的标准答案告知Shader,我们只用在试卷下写出答案,进行一些加工即可。
需要注意,MatCap Shader有一定的局限性。因为从某种意义上来说,基于MatCap的Shader,就是某种固定光照条件下,从某个特定方向,特定角度的光照表现结果。
正是因为是选择的固定的MatCap贴图,得到相对固定的整体光照表现,若单单仅使用MatCap,就仅适用于摄像机不调整角度的情形,并不适合摄像机会频繁旋转,调节角度的情形。但我们可以在某些Shader中,用MatCap配合与光照交互的其他属性,如将MatCap结合一个作为光照反射的颜色指导的Reflection
Cube Map,就有了与光照之间的交互表现。这样,就可以适当弥补MatCap太过单一整体光照表现的短板。
shader forge实现
unity中的效果:
UE4蓝图实现
ue4中的效果:
- ue和unity的亮度不一样啊…只能手动调参了,这里学习为主,暂时就不折腾这个了。
CubeMap
这个东西其实很多地方都讲过,如果感兴趣可以看看:
- games202的Precomputed Radiance Transfer的课
- 或者直接参考LearnOpenGl中IBL章节
- 庄老师PPT主要是讲了以反射形式为主的环境映射:
- 也就是我们要求的是那根绿线R。
SD和PS制作所需的贴图
下面说一下怎么用ps处理cubemap需要的贴图:
首先找到一张全景图,类似于这样的:
- 具体可以上subtance designer里面找3D View里的全景图,打开其中一个的文件夹,然后直接拖到ps里面:
- 拖进ps里后选择作为alpha通道:
- 选择:
- 调整图像大小:
- 然后设置图像模式,默认参数确认就行,这一步是为了将图片的亮度高于1和暗度低于0的部分限制在0到1范围内:
然后导出为tga格式:
- 导入到unity中,红色的需要调整,绿色的自己选择调不调整:
- 调整完应用之后就是下面这个样子,一个材质球:
shader forge实现
-
然后放下课程上的实现:
-
unity中自己的实现:
-
因为我们要得到的是视线看过去与法线形成的反射方向,所以需要获取到观察方向的反方向。
-
感兴趣的可以去了解下reflect函数的实现。
-
和课程上的实现图相比多了AO,也就是环境光遮蔽贴图的部分。
unity的效果图:
unity代码实现
Shader "shader forge/L9_CubeMap2"
{Properties{_normalmap ("normal map", 2D) = "bump" {}_cubeMap ("cubeMap", Cube) = "_Skybox" {}_AOTex ("AO Texture",2D) = "White" {}_n1 ("n1", Float ) = -1_MipMap_Level ("MipMap_Level", Range(0, 7)) = 0_fresnel_exp ("fresnel_exp", Range(0, 10)) = 1_EnvSpecInt ("EnvSpecInt", Range(0, 5)) = 1}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv0 : TEXCOORD0;float4 tangent : TANGENT;float3 normal : NORMAL;};struct v2f{float2 uv0 : TEXCOORD0; float4 pos : SV_POSITION;float4 posWorld : TEXCOORD1;float3 nDirWS : TEXCOORD2;float3 tDirWS : TEXCOORD3;float3 biDirWS : TEXCOORD4; };uniform sampler2D _normalmap;uniform samplerCUBE _cubeMap;uniform sampler2D _AOTex;uniform float _n1;uniform float _MipMap_Level;uniform float _fresnel_exp;uniform float _EnvSpecInt;v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv0 = v.uv0;o.nDirWS = UnityObjectToWorldNormal(v.normal);o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz,0.0)).xyz);o.biDirWS = normalize(cross(o.nDirWS,o.tDirWS) * v.tangent.w);o.posWorld = mul(unity_ObjectToWorld, v.vertex); return o;}fixed4 frag (v2f i) : SV_Target{//贴图采样float3 nDirTS = UnpackNormal(tex2D(_normalmap,i.uv0)).rgb;float AO_R = tex2D(_AOTex,i.uv0);//向量准备float3x3 TBN_Matrix = float3x3(i.tDirWS,i.biDirWS,i.nDirWS);float3 nDirWS_FT = normalize(mul(nDirTS,TBN_Matrix));float3 nDirVS_FT = normalize(mul(UNITY_MATRIX_V,float4(nDirWS_FT,0.0)).xyz);float3 vDirWS = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);float3 vrDirWS = normalize(reflect(-vDirWS,nDirWS_FT));//中间量准备fresnelfloat NoV = max(0.0,dot(nDirWS_FT,vDirWS));float OneMinusNoV = 1 - NoV;float fresnel = pow(OneMinusNoV,_fresnel_exp);cubemapfloat3 cubemap_uv = vrDirWS;float3 cubemap_color = texCUBElod(_cubeMap,float4(cubemap_uv,_MipMap_Level));//光照模型float3 finalColor = cubemap_color * fresnel;float3 EnvSpecLighting = finalColor * _EnvSpecInt * AO_R;//后处理//最后返回值return float4(EnvSpecLighting,1.0);}ENDCG}}
}
UE4蓝图实现(未实现)
额,虽然还想在ue4中实现一编,但是ue4的cubemap好像是直接用来制作天空盒的,跟本节课只是单纯作为贴图参与材质的编辑不一样,当然也有可能是我自己原因没有搜到相关教程,如果有大佬知道也可以在评论区告诉我一声。
如果对制作ue4的cubemap有兴趣的话也可移步至:
- Unreal Engine UE4虚幻引擎,生成Cubemap(HDR高动态范围贴图)
- UE4笔记:引擎内部制作cubemap步骤
- UE4_SkyLight立方体贴图的创建与应用