零、课程介绍
零、课程介绍
一、TA 基础
抽象的渲染过程
模型 > input > vertex shader > output > fragment shader > color
值结构g
乘
Lambert
HalfLambert
调子映射
将半 Lambert(0~1) 的调子视为 uv 坐标的 u 坐标,再附加一个常量作为 v 轴,得到的 uv 坐标对 RampTex 采样,假SSS的透光效果。
二、渲染技术
卡通渲染
常用技术 · Cell / ToonShading
规范
三、兰伯特光照模型
Lambert
Shader "AP1/L03/Lambert"
{
Properties {}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
Pass
{
Name "FORWARD"
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入结构
struct VertexInput
{
float4 vertex : POSITION; // 将模型顶点信息输入进来
float4 normal : NORMAL; // 将模型法线信息输入进来
};
// 输出结构
struct VertexOutput
{
float4 pos : SV_POSITION; // 由模型顶点信息换算而来的顶点屏幕位置
float3 nDirWS : TEXCOORD0; // 由模型法线信息换算来的世界空间法线信息
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert(VertexInput v)
{
VertexOutput o = (VertexOutput)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos(v.vertex); // 变换顶点信息并将其塞给输出结构
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线信息并将其塞给输出结构
return o; // 将输出结构输出
}
// 输出结构>>>像素
float4 frag(VertexOutput i) : COLOR
{
float3 nDir = i.nDirWS; // 获取nDir
float3 lDir = _WorldSpaceLightPos0.xyz; // 获取lDir
float nDotl = dot(i.nDirWS, lDir); // nDir点积lDir
float lambert = max(0.0, nDotl); // 截断负值
return float4(lambert, lambert, lambert, 1.0); // 输出最终颜色
}
ENDCG
}
}
FallBack "Diffuse"
}
四、半兰伯特光照模型
HalfLambert
// 输入结构
struct VertexInput
{
float4 vertex : POSITION; // 将模型顶点信息输入进来
float4 normal : NORMAL; // 将模型法线信息输入进来
};
// 输出结构
struct VertexOutput
{
float4 pos : SV_POSITION; // 由模型顶点信息换算而来的顶点屏幕位置
float3 nDirWS : TEXCOORD0; // 由模型法线信息换算来的世界空间法线信息
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert(VertexInput v)
{
VertexOutput o = (VertexOutput)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos(v.vertex); // 变换顶点信息 并将其塞给输出结构
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线信息 并将其塞给输出结构
return o; // 将输出结构 输出
}
// 输出结构>>>像素
float4 frag(VertexOutput i) : COLOR
{
float3 nDir = i.nDirWS; // 获取nDir
float3 lDir = _WorldSpaceLightPos0.xyz; // 获取lDir
float nDotl = dot(i.nDirWS, lDir); // nDir点积lDir
float halfLambert = nDotl * 0.5 + 0.5; // 映射至0~1
return float4(halfLambert, halfLambert, halfLambert, 1.0); // 输出最终颜色
}
预积分
预积分皮肤渲染:Pre-Integrated Skin Shading
2 维的 RampTex,可以根据不同的情况采样不同的 RampTex
五、OldSchool
漫反射和镜面反射
初中物理
镜面反射 : Specular Reflection
常用向量:(全要记)
• nDir:法线方向,点乘操作时简称n;
• lDir:光照方向,点乘操作时简称l;
• vDir:观察方向,点乘操作时简称v;
• rDir:光反射方向,点乘操作时简称r;
• hDir:半角方向(Halfway),lDir和vDir的中间角方向,点乘操作时简称h;
所在空间:(暂时只记WS,其余看热闹)
• OS:ObjectSpace 物体空间,本地空间;
• WS:WorldSpace 世界空间;
• VS: ViewSpace 观察空间;
• CS:HomogenousClipSpace 齐次剪裁空间;
• TS:TangentSpace 切线空间;
• TXS:TextureSpace 纹理空间;
例:nDirWS:世界空间下的法线方向;
漫反射-Diffuse:
因其向四面八方均匀散射,所以反射亮度和观察者看的方向无关;
实现方式:Lambert(n dot l),显然vDir不参与计算;
镜面反射-Specular:
因其反射有明显方向性,所以观察者的视角决定了反射光线的有无,明暗;
实现方式:
Phong(r dot v),即光反射方向和视角方向越重合,反射越强;
Blinn-Phong(n dot h),即法线方向和半角方向越重合,反射越强;
Old School
Lambert 漫反射 + Blinn-Phong 镜面反射
这样一个光照模型相对完善的Shader;这是一种上古套路,所以吾称之为OldSchool;
六、OldSchoolP
OldSchoolP
6. 像素Shader需要修改:
1. 向量准备:不需要hDir,但要追加rDir;rDir:光反射向量= reflect(-lDir, nDir);注意lDir是光方向的反方向;
2. 点积结果准备:不需要ndoth,追加vdotr;
3. 改blinnPhong为phong,phong=pow(max(0, vdotr), _SpecularPow);
BRDF
双向反射分布函数
想象你有一个不透明的桌面,一个激光发射器。你先让激光向下垂直地射在那个桌面上,这样你就 可以在桌面上看到一个亮点,接着你从各个不同的方向来观察那个亮点,你会发现亮点的亮度随着 观察方向的不同而发生了改变。然后你站着不动,改变激光发射方向和桌面的夹角,你又会发现亮 点的亮度发生了改变。这就是说,一个表面对不同的光线入射角和反射角的组合,拥有不同的反射 率。BRDF就是用来对这种反射性质进行定义的。---- 知乎用户
七、环境光和投影
环境光
三色环境光 ColAmbient
3. 定义面板参数:
• 贴图参数的定义方法:_XXX (“面板标签”, 2d) = “white” {}
• “white”{} 代表缺省纹理为纯白贴图,其他还有“black”{}“gray”{} …
9. 通过nDir计算朝上,朝下,侧面各部位遮罩;
10. 通过部位遮罩混合最终环境光颜色;
11. 采样Occlusion图,获得环境遮挡信息;采样贴图方法:tex2D(_Texture, uv);
投影
Unity 内置投影调用
6. 输出结构追加:LIGHTING_COORDS(0, 1):
• 此为Unity封装好的输出结构内容,照写就行,暂时不看细节;
• 括号中的参入,如(0, 1);0,1分别代表占用了TEXCOORD1和TEXCOORD2;
7. 顶点Shader中必须调用Unity封装好的方法:TRANSFER_VERTEX_TO_FRAGMENT(o);
8. 像素Shader中获取投影信息同样通过Unity提供的方法:LIGHT_ATTENUATION(i);
OldSchoolPlus
八、OldSchoolPlus
OldSchoolPlus
1. 分析构成:
• OldSchoolPlus=OldSchoolP+3ColAmbient+Shadow;
• 以OldSchoolP为模板,追加3ColAmbient,Shadow功能;
• OldSchoolPlus_SF作为参考;
7. 像素Shader
1. 向量,点积结果准备部分与OldSchoolP一致,不用改;
2. 光照模型拆为直接光照环境光照两部分:
1. 直接光照:在Lambert+Phong的基础上追加:
• 投影:算法可Copy自L07_Shadow;
• 光颜色:乘以_LightCol;
2. 环境光照:算法可Copy自L07_3ColAmbient;
3. 将直接光照和环境光照相加输出;
NormalMap 采样
5. 输入结构追加:
1. UV0:用于采样法线贴图;
2. Tangent:用于构建TBN矩阵;
6. 输出结构追加:
1. UV0:用于采样法线贴图;
2. tDirWS,bDirWS,nDirWS:切线空间3轴向方向,用于构建TBN矩阵;
九、菲涅尔项
菲涅尔现象
真实世界中,除了金属之外其它物质,视线垂直于表面时,反射较弱,而当视线非垂直表面时,夹角越小,反射越明显。
Matcap
一种无视BRDF,将BRDF渲染结果,用View空间法线朝向,直接映射到模型表面的流氓算法;
1. 将nDir从切线空间转到观察空间;
2. 取RG通道Remap到(0~1),作为UV对Matcap图采样;
3. 叠加菲涅尔效果,以模拟金属和非金属不同质感;
Cubemap
采样Cubemap;
• 采样方法:texCUBElod(_Cubemap, float4);
• Float4参数:xyz:vrDir;w:mip等级;
十、OldSchoolPro
OldSchoolPro
3. 定义面板参数
• [Header(XXX)] 排版
4. 为产生投影,包含以下文件:
• AutoLight.cginc
• Lighting.cginc
十一、Shader 常用的写作技巧
面板参数声明格式
数值,范围:
• _Name (“标签名”, float) = defaultVal
• _Name (“标签名”, range(min, max)) = defaultVal
• _Name (“标签名”, int) = defaultVal
位置,向量,颜色:
• _Name (“标签名”, vector) = (xVal, yVal, zVal, wVal)
• _Name (“标签名”, color) = (rVal, gVal, bVal, aVal)
2D,3D纹理,环境球:
• _Name (“标签名”, 2d) = “defaultTex” {}
• _Name (“标签名”, 3d) = “defaultTex” {}
• _Name (“标签名”, cube) = “defaultTex” {}
参数属性
[HideInInspect]
• 用途:在面板上隐藏该参数;
• 可用于:任何参数;
• 例:[HideInInspect] _FakeLightDir (“伪光方向”, vector) = (0.0, 1.0, 0.0, 1.0)
[NoScaleOffset]
• 用途:禁用纹理的TilingOffset面板;不需要做TilingOffset的纹理,比如大部分的角色纹理,防止美术误设置;
• 可用于:纹理参数;
• 例:[NoScaleOffset] _MainTex (“主贴图”, 2d) = “white” {}
[Normal]
• 用途:标示该纹理参数为法线贴图,以激活相关自检功能;
• 可用于:2D纹理参数;
• 例:[Normal] _NormTex (“法线贴图”, 2d) = “bump”{}
[HDR]
• 用途:用于设置高动态范围颜色值;如:灯光颜色,自发光颜色等;
• 可用于:颜色参数;
• 例:[HDR] _EmitCol (“自发光颜色”, color) = (1.0, 1.0, 1.0, 1.0)
[Gamma]
• 用途:用于颜色参数的色彩空间的转换;一般用于色彩空间为Linear的项目;
• 可用于:颜色参数;
• 例:[Gamma] _EmitCol (“自发光颜色”, color) = (1.0, 1.0, 1.0, 1.0)
[PowerSlider(value)]
• 用途:对范围参数做Power处理后再传入Shader;纠正部分参数调节手感;
• 可用于:范围参数;
• 例:[PowerSlider(0.5)] _SpecPow (“高光次幂”, range(1, 90)) = 30
[Header(Label)]
• 用途:标签,用于排版;
• 可用于:单独使用;
• 例:[Header(Texture)]
[Space(value)]
• 用途:空行,用于排版;
• 可用于:单独使用;
• 例:[Space(50)]
其他:[Toggle] [Enum] [Keyword] 配合宏使用,暂时不用知道;自定义Drawer需要一定C#能力,暂时不用知道;
参数类型
• fixed: 11位定点数,-2.0~2.0,精度1/256;
• half: 16位浮点数,-60000~60000,精度约3位小数;
• float: 32位浮点数,-3.4E38~3.4E38,精度约6,7位小数;
• Int: 32位整形数,较少使用;
• bool: 布尔型数,较少使用;
• 矩阵:
• float2x2, float3x3, float4x4, float2x3 诸如此类格式;
• half2x2, half3x3, half4x4, half2x3 诸如此类格式;
• 纹理对象:
• sampler2D: 2D纹理
• sampler3D: 3D纹理
• samplerCUBE: Cube纹理
精度选择
• 原则上优先使用精度最低的数据类型;
• 经验:
• 世界空间位置和UV坐标,使用float;
• 向量,HDR颜色,使用half;视情况升到float;
• LDR颜色,简单乘子,可使用fixed;
可访问的顶点 Input 数据
• POSITION 顶点位置float3 float4
• TEXCOORD0 UV通道1 float2 float3 float4
• TEXCOORD1 UV通道2 float2 float3 float4
• TEXCOORD2 UV通道3 float2 float3 float4
• TEXCOORD3 UV通道4 float2 float3 float4
• NORMAL 法线方向float3
• TANGENT 切线方向float4
• COLOR 顶点色float4
常用的顶点 Output 数据
• pos 顶点位置CS float4
• uv0 一般纹理UV float2
• uv1 LighmapUV float2
• posWS 顶点位置WS float3
• nDirWS 法线方向WS half3
• tDirWS 切线方向WS half3
• bDirWS 副切线方向WS half3
• color 顶点色fixed4
常用的顶点 Shader 操作
注:Unity2019.3.2f1版本
• pos o.pos = UnityObjectToClipPos(v.vertex);
• uv0 o.uv0 = v.uv0; o.uv0 = TRANSFORM_TEX(v.uv0, _MainTex);
• uv1 o.uv1 = v.uv1; o.uv1 = v.uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
• posWS o.posWS = mul(unity_ObjectToWorld, v.vertex);
• nDirWS o.nDirWS = UnityObjectToWorldNormal(v.normal);
• tDirWS o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz);
• bDirWS o.bDirWS = normalize(cross(o.nDirWS, o.tDirWS) * v.tangent.w);
• color o.color = v.color;
十二、渲染大作业
双头食人魔渲染
效果图
纹理资源
资源优化
光照模型
Code
Prepare
Properties
Properties {
_MainTex ("RGB:颜色A:透贴", 2d) = "white"{}
_MaskTex ("R:高光强度G:边缘光强度B:高光染色A:高光次幂", 2d) = "black"{}
_NormTex ("RGB:法线贴图", 2d) = "bump"{}
_MatelnessMask ("金属度遮罩", 2d) = "black"{}
_EmissionMask ("自发光遮罩", 2d) = "black"{}
_DiffWarpTex ("颜色Warp图", 2d) = "gray"{}
_FresWarpTex ("菲涅尔Warp图", 2d) = "gray"{}
_Cubemap ("环境球", cube) = "_Skybox"{}
}
property input
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex;
uniform sampler2D _MaskTex;
uniform sampler2D _NormTex;
uniform sampler2D _MatelnessMask;
uniform sampler2D _EmissionMask;
uniform sampler2D _DiffWarpTex;
uniform sampler2D _FresWarpTex;
uniform samplerCUBE _Cubemap;
向量准备
half3 nDirTS = UnpackNormal(tex2D(_NormTex, i.uv0));
half3x3 TBN = half3x3(i.tDirWS, i.bDirWS, i.nDirWS);
half3 nDirWS = normalize(mul(nDirTS, TBN));
half3 vDirWS = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
half3 vrDirWS = reflect(-vDirWS, nDirWS);
half3 lDirWS = _WorldSpaceLightPos0.xyz;
half3 lrDirWS = reflect(-lDirWS, nDirWS);
// 中间量准备
half ndotl = dot(nDirWS, lDirWS);
half ndotv = dot(nDirWS, vDirWS);
half vdotr = dot(vDirWS, lrDirWS);
纹理采样
// 采样纹理
half4 var_MainTex = tex2D(_MainTex, i.uv);
half4 var_MaskTex = tex2D(_MaskTex, i.uv);
half var_MatelnessMask = tex2D(_MatelnessMask, i.uv).r;
half var_EmissionMask = tex2D(_EmissionMask, i.uv).r;
half3 var_FresWarpTex = tex2D(_FresWarpTex, ndotv).rgb;
half3 var_Cubemap = texCUBElod(_Cubemap, float4(vrDirWS, lerp(8.0, 0.0, var_MaskTex.a))).rgb;
// 提取信息
half3 baseCol = var_MainTex.rgb;
half opacity = var_MainTex.a;
half specInt = var_MaskTex.r;
half rimInt = var_MaskTex.g;
half specTint = var_MaskTex.b;
half specPow = var_MaskTex.a;
half matellic = var_MatelnessMask;
half emitInt = var_EmissionMask;
half3 envCube = var_Cubemap;
half shadow = LIGHT_ATTENUATION(i);
Light Mode
DiffCol SpecCol
// 漫反射颜色镜面反射颜色
half3 diffCol = lerp(baseCol, half3(0.0, 0.0, 0.0), matellic);
half3 specCol = lerp(baseCol, half3(0.3, 0.3, 0.3), specTint) * specInt;
Fresnel Power
// 菲涅尔强度
half3 fresnel = lerp(var_FresWarpTex, 0.0, matellic);
half fresnelCol = fresnel.r; // 无实际用途
half fresnelRim = fresnel.g;
half fresnelSpec = fresnel.b;
DirDiff
// 光源漫反射
half halfLambert = ndotl * 0.5 + 0.5;
half3 var_DiffWarpTex = tex2D(_DiffWarpTex, half2(halfLambert, 0.2));
half3 dirDiff = diffCol * var_DiffWarpTex * _LightCol;
DirSpec
// 光源镜面反射
half phong = pow(max(0.0, vdotr), specPow * _SpecPow);
half spec = phong * max(0.0, ndotl);
spec = max(spec, fresnelSpec);
spec = spec * _SpecInt;
half3 dirSpec = specCol * spec * _LightCol;
EnvDiff
// 环境漫反射
half3 envDiff = diffCol * _EnvCol * _EnvDiffInt;
EnvSpec
// 环境镜面反射
half reflectInt = max(fresnelSpec, matellic) * specInt;
half3 envSpec = specCol * reflectInt * envCube * _EnvSpecInt;
RimLight
// 轮廓光
half3 rimLight = _RimCol * fresnelRim * rimInt * max(0.0, nDirWS.g) * _RimInt;
Emission
// 自发光
half3 emission = diffCol * emitInt * _EmitInt;
最终混合
// 混合
half3 finalRGB = (dirDiff + dirSpec) * shadow + envDiff + envSpec + rimLight + emission;
其他细节
Clip
// 透明剪切
clip(opacity - _Cutoff);
透明剪切投影修正
如果是透明就丢弃该片段的渲染,但是还是需要写入 ShadowMap,退回到一个简单的也能写入 ShadowMap 的 Shader
// 声明回退Shader
FallBack "Legacy Shaders/Transparent/Cutout/VertexLit"
双面显示
PassTags 后声明 Cull 模式 - Cull Off;
效果完成
开源 Shader
SP,SD都有 GLSL 的 Shader 源码
十三、特效
特效分类
透切 AlphaCutout :AC
渲染实体,透明度小于阈值,丢弃该片段渲染
Tags {
"RenderType"="TransparentCutout" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
half4 var_MainTex = tex2D(_MainTex, i.uv); // 采样贴图RGB颜色A透贴
clip(var_MainTex.a - _Cutoff); // 透明剪切
透混 AlphaBlend AB
Tags {
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
Blend One OneMinusSrcAlpha // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
透叠
Blend One One // 修改混合方式
更多混合模式
Shader 面板
Properties {
_MainTex ("RGB:颜色A:透贴", 2d) = "gray"{}
[Enum(UnityEngine.Rendering.BlendMode)]
_BlendSrc ("混合源乘子", int) = 0
[Enum(UnityEngine.Rendering.BlendMode)]
_BlendDst ("混合目标乘子", int) = 0
[Enum(UnityEngine.Rendering.BlendOp)]
_BlendOp ("混合算符", int) = 0
}
BlendOp [_BlendOp] // 可自定义混合算符
Blend [_BlendSrc] [_BlendDst] // 可自定义混合模式
常用混合模式
十四、无
十五、透明问题和 UV 流动
UV 流动
GhostFlow
o.uv0 = v.uv; // uv0 为原生 uv
o.uv1 = TRANSFORM_TEX(v.uv, _WarpTex); // UV1支持TilingOffset
o.uv1.y = o.uv1.y + frac(-_Time.x * _FlowSpeed);// UV1 用来流动 UV
// 噪声处理
half3 finalRGB = var_MainTex.rgb;
half noise = lerp(1.0, var_NoiseTex * 2.0, _NoiseInt); // Remap噪声
noise = max(0.0, noise); // 截去负值
half opacity = var_MainTex.a * _Opacity * noise;
UV 扰动
GhostWrap
vertex shader 与 GhostFlow 类似
// 使用 WarpTex 来扰动 UV
half3 var_WarpTex = tex2D(_WarpTex, i.uv1).rgb; // 噪声图
float2 uvBias = (var_WarpTex - 0.5) * _WarpInt; // 计算UV偏移值
float2 uv0 = i.uv0 + uvBias; // 应用UV偏移量
half4 var_MainTex = tex2D(_MainTex, uv0); // 偏移后UV采样MainTex
十六、无
十七、屏幕 UV
屏幕纹理采样
ScreenUV
uniform sampler2D _ScreenTex; uniform float4 _ScreenTex_ST;
// vert
float3 posVS = UnityObjectToViewPos(v.vertex).xyz; // 顶点位置OS>VS
float originDist = UnityObjectToViewPos(float3(0.0, 0.0, 0.0)).z; // 原点位置OS>VS
o.screenUV = posVS.xy / posVS.z; // VS空间畸变校正
o.screenUV *= originDist; // 纹理大小按距离锁定
o.screenUV = o.screenUV * _ScreenTex_ST.xy - frac(_Time.x * _ScreenTex_ST.zw); // 启
用屏幕纹理ST
// frag
half var_ScreenTex = tex2D(_ScreenTex, i.screenUV).r; // 采样屏幕纹理
屏幕扰动
ScreenWarp
// 获取背景纹理
GrabPass {
"_BGTex"
}
uniform sampler2D _BGTex;
// vert
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex); // 顶点位置OS>CS
o.uv = v.uv; // UV信息
o.grabPos = ComputeGrabScreenPos(o.pos); // 背景纹理采样坐标
十八、走马灯与极坐标效果
走马灯与极坐标效果
十九、顶点运算
顶点移动
AB基础上,追加Y轴向上周期性位移;
// 声明常量
#define TWO_PI 6.283185
// 顶点动画方法
void Translation (inout float3 vertex) {
vertex.y += _MoveRange * sin(frac(_Time.z * _MoveSpeed) * TWO_PI);
}
1. 常量声明方法:#define 常量名常量值
2. void声明无返回值方法;
3. Inout修饰:参数的出入证;
4. frac(…):浮点精度保护;
5. sin(…):产生波动的常用方法;
6. 计算偏移值,并加到顶点对应轴,以实现平移效果;
AB基础上,混合各种基本动画形式为复杂动画;
• 动画拆分:
1. 天使圈:缩放;
2. 身子:X轴摆动,Z轴摆动;
3. 头部:Y轴旋转;
4. 全身:Y轴起伏。
1. _ScaleParams:天使圈缩放相关参数;
2. _SwingXParams:X轴扭动相关参数;
3. _SwingZParams:Z轴扭动相关参数;
4. _SwingYParams:Y轴起伏相关参数;
5. _ShakeYParams:Y轴摇头相关参数;
顶点旋转
// 声明常量
#define TWO_PI 6.283185
// 顶点动画方法
void Rotation (inout float3 vertex) {
float angleY = _RotateRange * sin(frac(_Time.z * _RotateSpeed) * TWO_PI);
float radY = radians(angleY);
float sinY, cosY = 0;
sincos(radY, sinY, cosY);
vertex.xz = float2(
vertex.x * cosY - vertex.z * sinY,
vertex.x * sinY + vertex.z * cosY
);
}
幽灵动画
AnimGhost
1. 天使圈:用顶点色遮罩缩放动画;校正放缩后的位置;
2. 摆动:用Y轴值偏移出正弦波摆动;用顶点色遮罩摆动;
3. 摇头:用顶点色遮罩头部实现摇头;用顶点色实现天使圈滞后;
4. 起伏:用顶点色实现天使圈滞后;
5. 顶点色:计算天使圈亮度值;
二十、钟表小人
钟表小人 Shader
建模时用顶点色涂抹指针位置,表示哪些顶点是时针、分针、秒针
时间同步系统时间
C# 代码当前系统时间计算好角度传入材质给着色器计算
二十一、赛博小人
特效纹理
EffectMap : EffMap
二十二、Unity内置Lightmap的使用
LightMap
Lightmap 作为记录光照结果或部分光照结果的一种载体方式,常用来替代 实时渲染中性能昂贵的计算部分
场景搭建
资产准备
场景免费资产
天空资产:AllSky Free
基于Unity Skybox/Cubemap Shader;
静态 GameObject 设置
Nothing:全不选;
Everything:全选;
Contribute GI:响应全局光照;
Occluder/ Occludee Static:响应OccCulling;
Batching Static:响应合批;
Navigation Static/Off Mesh Link Generation:响应导航;
Reflection Probe Static:响应反射探头;
课程案例中:
仅渲染效果相关,启用ContributeGI,ReflectionProbeStatic即可;
Lightmapping Setting 中的烘培设置
Lightmap Resolution为烘培精度,可以调低预览调整打光,确定后调高输出成品;
DirectionalMode手游一般不开启,效果改善有限,Lightmap翻倍;
合批
合并渲染,减少渲染批次,以优化性能; 常用策略:
Unity提供的SRPBatching(SRP管线支持);
二十三、其他软件生成 Lightmap 的使用
Corona2 烘培 LightMap
SD 自定义 Lightmap 贴图
手写建筑物 Shader
Building
10.法线解码方法: • 纹理中法线信息值域为(01),映射到(-11); • 根据法线信息length为1的特性,求出nDirTS;
// 法线信息解码方法
float3 DecodeNormal(float2 maskXY) {
float2 nDirTSxy = maskXY * 2.0 - 1.0;
float nDirTSz = sqrt(1.0 - nDirTSxy.x * nDirTSxy.x + nDirTSxy.y * nDirTSxy.y);
return float3(nDirTSxy, nDirTSz);
}