学习资料:Unity Shader入门精要
时长:一个月
计划:1~2天完成一章,预计一个月内完成
第二章 渲染流水线
- 性能瓶颈是消耗最大的流程
- 渲染流水线:应用阶段→几何阶段→光栅化阶段
- 应用阶段:CPU实现,输出渲染图元
- 数据加载到显存中:显卡对显存(VRAM)的访问速度比对内存(RAM)要快,并且大多数显卡对RAM没有访问权限
- 设置渲染状态:定义场景中的网格是怎样被渲染的,例如使用哪个shader
- 调用DrawCall:CPU通过调用DrawCall告诉GPU开始一个渲染过程,一个DrawCall会指向本次渲染图元列表
- 几何阶段:GPU实现,输出屏幕空间的二位点坐标,深度,着色信息,传送给光栅化阶段
- 顶点着色器:对输入的每个顶点都会调用一次顶点着色器,而且无法得到顶点与顶点之间的关系。顶点着色器的主要功能是坐标变换:把顶点坐标从模型空间转换到齐次裁剪空间。再由硬件进行透视除法,最终得到归一化的设备坐标(NDC)
- 裁剪:裁剪删除不在摄像机范围内的顶点
- 屏幕映射:将,x和y转到屏幕坐标系,z坐标无作为,但会被一起传进光栅化后发挥作用
- 光栅化阶段:GPU实现,决定每个渲染图元哪些像素应该被绘制在屏幕上,将几何阶段传来的顶点数据进行插值
- 三角形设置:计算光栅化一个三角网格所需的信息
- 三角形遍历:检查每个像素是否被一个三角网格所覆盖,覆盖,则赋予片元着色器
- 片元着色器:对从顶点着色器来的数据进行插值数据完成很多渲染技术:最重要的一个是纹理采样
- 逐片元操作:合并数据+操作。通过测试来决定每个片元的可见性。半透明物体必须开启混合操作
- 应用阶段:CPU实现,输出渲染图元
第三章 UnityShader基础
- 在材质面板上显示更多类型的变量,例如自定义一个bool值
Shader "MyTest/TestShaderGUI" { Properties { _MainTex("Texture", 2D) = "white" {} // 声明需要的控件 [Toggle(S_BOOL)] _S_BOOL("S_BOOL", Int) = 0 [Toggle] _MyToggle1("MyToggle1", Float) = 0 [Toggle(MyToggle2)] _MyToggle2("MyToggle2", Float) = 0 [KeywordEnum(One, Two, Three)] _MyEnum("MyEnum", Float) = 0 } SubShader { Tags{ "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert addshadow // 创建变量,用来接收控件的值 #pragma shader_feature S_BOOL #pragma shader_feature _MYTOGGLE1_ON #pragma shader_feature MyToggle2 #pragma multi_compile _MYENUM_ONE _MYENUM_TWO _MYENUM_THREE sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf(Input IN, inout SurfaceOutput o) { half4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; #if S_BOOL o.Albedo.gb *= 0.5; #endif #if _MYTOGGLE1_ON o.Albedo.gb *= 0.5; #endif #if MyToggle2 o.Albedo.gb *= 0.5; #endif #if _MYENUM_ONE o.Albedo.gb *= 0.2; #elif _MYENUM_TWO o.Albedo.gb *= 0.5; #elif _MYENUM_THREE o.Albedo.gb *= 0.7; #endif } ENDCG } }
第四章 学习Shader所需的数学基础
- 顶点着色器的最基本任务就是把顶点坐标从模型空间转换到裁剪空间中
- 实现点/矢量的平移,旋转,缩放。顺序是固定的:先缩放→旋转→平移
第五章 开始UnityShder学习之旅
- 顶点着色器是逐顶点执行的。v:POSITION语义告诉Unity把模型空间中的顶点坐标赋值给v。SV_POSITION告诉Unity顶点着色器的输出是裁剪空间中的顶点坐标,如果自定义了结构体,就不需要写。SV_TARGET告诉渲染器把用户的输出颜色存储到一个渲染缓冲区中
- NORMAL语义获得顶点的法线方向,用于计算光照。TEXCOORD0语义获得模型顶点的纹理坐标,来访问纹理
-
Shader "Unlit/chapter5-simplerShader" { Properties{ _Color("Color Tint",Color) = (1.0,1.0,1.0,1.0) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag fixed4 _Color;//每个属性都要在CGPROGRAM后声明才可以使用 struct a2v{//模型空间顶点着色器 float4 _vertex : POSITION; float3 _normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f{//顶点着色器到片元着色器,用于两者间的通信 float4 _pos : SV_POSITION; fixed3 _color : COLOR0; }; v2f vert (a2v v)//由于返回的是v2f,就不需要写SV_POSITION { v2f o; o._pos = UnityObjectToClipPos(v._vertex);//unity5.6之后等同于mul(UNITY_MATRIX_MVP,v._vertex); o._color = v._normal * 0.5 + fixed3(0.5,0.5,0.5);//将color限制在[0,1]之间 return o; } fixed4 frag (v2f i) : SV_Target { fixed3 c=i._color; c *= _Color.rgb;//使用Color来控制输出颜色 return fixed4(c,1.0); } ENDCG } } }
- 疑问:为何存在物理渲染?还有非物理渲染吗?p109可以由我们自己决定修饰的变量含义,是什么意思?开启抗齿距的情况下,可能会在不同平台下造成图像倒立的情况,要在代码中解决这个问题
- 语义表达了某个参数的含义,通俗地讲,这些语义可以让Shader知道从哪里读取数据,并把数据输出到哪里。语义出现的位置不同,效果不同
- 对于一些特殊的语义,我们要以SV开头,为了更好的跨平台性。例如,SV_POSITION(POSITION)代表输出裁剪空间的顶点坐标,SV_Target(COLOR)输出到渲染目标。
- 可以使用Visual studio Graphics Debugger进行调试,可以看到某一帧某一个像素点的RGB等等参数。EXE包→空项目→项目属性添加Commend路径和变量,代码块中加:#pragma enable_d3d11_debug_symbols。看官方文档细心看还是能明白的,要有意培养看英文文档的习惯
- 在移动平台上,可以使用fixed类型来存储颜色和单位适量。如果我们的目标平台是移动平台,一定要在真机上测试Shader的效果,因为在windows上能成功运行,不代表能在手机上成功运行
- Unity支持的各种ShaderTarget版本越高,对应Shader的能力就越大,能使用的寄存器,指令数就越多。但最好的方式是进行优化,尽可能的减少性能的消耗
- GPU使用了不同于CPU的技术来实现分支语句,在最坏的情况下,花在一个分支的时间可能等于运行了所有分支的时间,一因此不鼓励在Shader中使用流程控制语句
第六章 Unity中的基础光照
顶点v法线的获取方式:将所有用到该顶点v的三角形平面求出对应的法向量,再拟合,求单位化
漫反射[兰伯特模型,背光区像平面]
- 公式:
- 逐顶点[存在锯齿,得使用逐像素解决]
//该代码只适合于只有一个光源的情况,如果多光源,_WorldSpaceLightPos0就不能得到正确的结果 Shader "Unity Shader Book/Chapter 6/Diffuse Vertex-Level"{ Properties{ _Diffuse("Diffuse",Color) = (1,1,1,1) } SubShader{ //顶点/片元着色器需要写在Pass语义块中 pass{ Tags{"LightMode" = "ForwardBase"}//只有定义了正确的LightMode才能获得Unity内置的光照变量,例如_LightColor0 CGPROGRAM #pragma vertex vert #pragma fragment frag fixed4 _Diffuse; struct a2v{ float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f{ float4 pos : SV_POSITION; float3 color: COLOR; }; #include"Lighting.cginc" v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //获得环境光 fixed3 VertexWorldNormal = normalize(mul(unity_ObjectToWorld,v.normal)); fixed3 VertexWorldLight = normalize(_WorldSpaceLightPos0.xyz); //光源方向 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(VertexWorldNormal,VertexWorldLight)); o.color = ambient + diffuse; return o; } fixed4 frag(v2f i):SV_TARGET0{ return fixed4(i.color,1.0); } ENDCG } } FallBack "Diffuse" }
- 逐像素
//该代码只适合于只有一个光源的情况,如果多光源,_WorldSpaceLightPos0就不能得到正确的结果 Shader "Unity Shader Book/Chapter 6/Diffuse Pixel-Level"{ Properties{ _Diffuse("Diffuse",Color) = (1,1,1,1) } SubShader{ //顶点/片元着色器需要写在Pass语义块中 pass{ Tags{"LightMode" = "ForwardBase"}//只有定义了正确的LightMode才能获得Unity内置的光照变量,例如_LightColor0 CGPROGRAM #pragma vertex vert #pragma fragment frag fixed4 _Diffuse; struct a2v{ float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f{ float4 pos : SV_POSITION; float3 PixelWorldNormal: NORMAL; }; #include"Lighting.cginc" v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.PixelWorldNormal = mul(_Object2World,v.normal); return o; } fixed4 frag(v2f i):SV_TARGET0{ fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //获得环境光 fixed3 PixelWorldNormal = i.PixelWorldNormal; fixed3 PixelWorldLight = normalize(_WorldSpaceLightPos0.xyz); //光源方向 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(PixelWorldNormal,PixelWorldLight)); fixed3 m_color = ambient + diffuse; return fixed4(m_color,1.0); } ENDCG } } FallBack "Diffuse" }
- 半兰伯特模型[背光面有明暗变化,没有任何物理依据,仅仅是为了增强视觉效果]
//该代码只适合于只有一个光源的情况,如果多光源,_WorldSpaceLightPos0就不能得到正确的结果 Shader "Unity Shader Book/Chapter 6/Diffuse HalfLambert-Level"{ Properties{ _Diffuse("Diffuse",Color) = (1,1,1,1) } SubShader{ //顶点/片元着色器需要写在Pass语义块中 pass{ Tags{"LightMode" = "ForwardBase"}//只有定义了正确的LightMode才能获得Unity内置的光照变量,例如_LightColor0 CGPROGRAM #pragma vertex vert #pragma fragment frag fixed4 _Diffuse; struct a2v{ float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f{ float4 pos : SV_POSITION; float3 PixelWorldNormal: NORMAL; }; #include"Lighting.cginc" v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.PixelWorldNormal = mul(_Object2World,v.normal); return o; } fixed4 frag(v2f i):SV_TARGET0{ fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //获得环境光 fixed3 PixelWorldNormal = i.PixelWorldNormal; fixed3 PixelWorldLight = normalize(_WorldSpaceLightPos0.xyz); //光源方向 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb *(dot(PixelWorldNormal,PixelWorldLight) * 0.5f + 0.5f); fixed3 m_color = ambient + diffuse; return fixed4(m_color,1.0); } ENDCG } } FallBack "Diffuse" }
高光反射[镜面反射,冯氏光照模型]
- 逐顶点
Shader "Unity Shader Book/Chapter 6/Specular Vertex-Level" { Properties{ _Diffuse("Diffuse",Color) = (1,1,1,1) _Specular("Specular",Color) = (1,1,1,1) _Gloss ("Gloss",Range(8.0,256.0)) = 20.0 } SubShader{ pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; fixed4 _Specular; float _Gloss; struct a2v{ float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f{ float4 pos : SV_POSITION; fixed3 color : COLOR; }; v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //获得环境光 fixed3 VertexWorldNormal = normalize(mul(unity_ObjectToWorld,v.normal)); fixed3 VertexWorldLight = normalize(_WorldSpaceLightPos0.xyz); //光源方向 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(VertexWorldNormal,VertexWorldLight)); float3 reflectDir = normalize(reflect(VertexWorldLight,VertexWorldNormal)); float3 ViewerDir = normalize(_WorldSpaceCameraPos.xyz - (mul(unity_ObjectToWorld,v.vertex)).xyz); float3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(ViewerDir,reflectDir)),_Gloss); o.color = ambient + diffuse + specular; return o; } fixed4 frag(v2f i):SV_TARGET0{ return fixed4(i.color,1.0); } ENDCG } } FallBack "Specular" }
- 逐像素[更加平滑]
Shader "Unity Shader Book/Chapter 6/Specular Vertex-Level" { Properties{ _Diffuse("Diffuse",Color) = (1,1,1,1) _Specular("Specular",Color) = (1,1,1,1) _Gloss ("Gloss",Range(8.0,256.0)) = 20.0 } SubShader{ pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; fixed4 _Specular; float _Gloss; struct a2v{ float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f{ float4 pos : SV_POSITION; fixed3 WorldNormal : TEXCOORD0; fixed3 WorldLight : TEXCOORD1; fixed3 WorldPos : TEXCOORD2; }; v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.WorldNormal = normalize(mul(unity_ObjectToWorld,v.normal)); o.WorldLight = normalize(_WorldSpaceLightPos0.xyz); //光源方向 o.WorldPos = mul(unity_ObjectToWorld,v.vertex).xyz; return o; } fixed4 frag(v2f i):SV_TARGET0{ fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //获得环境光 fixed3 VertexWorldNormal = i.WorldNormal; fixed3 VertexWorldLight = i.WorldLight; fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(VertexWorldNormal,VertexWorldLight)); float3 reflectDir = normalize(reflect(VertexWorldLight,VertexWorldNormal)); float3 ViewerDir = normalize(_WorldSpaceCameraPos.xyz - i.WorldPos); float3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(ViewerDir,reflectDir)),_Gloss); float3 m_color = ambient + diffuse + specular; return fixed4(m_color,1.0); } ENDCG } } FallBack "Specular" }
第七章 基础纹理
- 单张纹理[使用纹理来代替漫反***色]
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Unity Shaders Bookj/Chapter 7/Single Texture"{ Properties{ _Color("Color Tint",Color) = (1,1,1,1) _MainTex("Main Tex",2D) = "white" {} _Specular("Specular",Color) = (1,1,1,1) _Gloss("Gloss",Range(8.0,256)) = 20 } SubShader{ pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Specular; float _Gloss; struct a2v{ float4 vertex : POSITION; float3 normal :NORMAL; float4 texcoord :TEXCOORD0; }; struct v2f{ float4 pos : SV_POSITION; float3 WorldNormal:TEXCOORD0; float3 WorldPos : TEXCOORD1; float2 uv :TEXCOORD2; }; v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.WorldNormal = mul(unity_ObjectToWorld,v.normal); o.WorldPos = mul(unity_ObjectToWorld,v.vertex); o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; return o; } //一个疑问,为何把albedo参杂到漫反射和环境光计算,而不是高光反射???? fixed4 frag(v2f i):SV_TARGET0{ fixed3 WorldNormal = normalize(i.WorldNormal); fixed3 WorldLightDir = normalize(UnityWorldSpaceLightDir(i.WorldPos)); fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb; // tex2D对纹理进行采样 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; //以下是公式计算 fixed3 diffuse = _LightColor0.rgb * albedo*max(0,dot(WorldNormal,WorldLightDir)); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.WorldPos)); fixed3 halfDir = normalize(WorldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb*pow(max(0,dot(WorldNormal,halfDir)),_Gloss); return fixed4(ambient + diffuse + specular , 1.0); } ENDCG } } FallBack "Diffuse" }
- Warp Mode(纹理放大) : 决定当纹理坐标超过[0,1]区间后如何显示。Repeat保留小数,因此会重复;Clamp为截取函数
- Filter Mode : Point,Bilinear以及Trilinear三种模式,效果依次提升,但需要耗费的性能也就越大
- 多级渐远纹理技术(纹理缩小,Advanced,勾选Generate Mip Maps): 将原纹理提前使用滤波技术得到很多图像,每一层都是对上一层图像降采样的过程,形成了一个图像金字塔。类似ACM中的预处理输出,再根据距离相机的dis来决定使用res[???],用空间换时间。
- 纹理尽量使用2幂次的长宽,使用NPOT的图,会造成性能的下降
- 凹凸映射
- 切线空间计算
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space"{ Properties{ _Color("Color Tint",Color) = (1,1,1,1) _MainTex("Main Tex",2D) = "white" {} _Specular("Specular",Color) = (1,1,1,1) _Gloss("Gloss",Range(8.0,256)) = 20 _BumpMap("Normal Map",2D) = "bump"{} _BumpScale("Bump Scale",float) = 1.0 } SubShader{ pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpMap; float4 _BumpMap_ST; float _BumpScale; fixed4 _Specular; float _Gloss; struct a2v{ float4 vertex : POSITION; float3 normal :NORMAL; float4 tangent : TANGENT; float4 texcoord :TEXCOORD0; }; struct v2f{ float4 pos : SV_POSITION; float4 uv :TEXCOORD0; float3 lightDir:TEXCOORD1; float3 viewDir:TEXCOORD2; }; v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw; float3 binormal = cross(normalize(v.normal),normalize(v.tangent.xyz)) * v.tangent.w; float3x3 rotation = float3x3(v.tangent.xyz,binormal,v.normal); o.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)).xyz; o.viewDir = mul(rotation,ObjSpaceViewDir(v.vertex)).xyz; return o; } fixed4 frag(v2f i):SV_TARGET0{ fixed3 tangentLightDir = normalize(i.lightDir); fixed3 tangentViewDir = normalize(i.viewDir); fixed3 tangentNormal; fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw); tangentNormal.xy = (packedNormal.xy*2-1)*_BumpScale; tangentNormal.z = sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy))); fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; fixed3 diffuse = _LightColor0.rgb* albedo * max(0,dot(tangentNormal,tangentLightDir)); fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss); return fixed4(ambient + diffuse + specular , 1.0); } ENDCG } } FallBack "Diffuse" }
- 世界空间计算
- 一个插值寄存器最多只能存储float4大小的变量。该方法核心思想是:把三维传到片元着色器进行计算。
- 对于传值我一直是有疑问的,到底在顶点着色器需要计算那些?在片元需要计算那些?唯一能确定的是在片元中进行光照计算效果要比在顶点着色器中要更平滑,视觉效果更好。
- 切线空间计算
- 渐变纹理[更加灵活地控制漫反射光照结果],贴图的Wrap Mode设为Clamp模式,防止由于精度对显示造成的影响
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Ramp Texture"{ Properties{ _Color("Color Tint",Color) =(1,1,1,1) _RampTex("Ramp Tex",2D) = "white"{} _Specular ("Specular",Color) = (1,1,1,1) _Gloss("Gloss",Range(8.0,256)) = 20 } SubShader{ pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag fixed4 _Color; sampler2D _RampTex; float4 _RampTex_ST; fixed4 _Specular; float _Gloss; struct a2v{ fixed4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f{ fixed4 pos : SV_POSITION; float3 WorldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; #include "Lighting.cginc" v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); //o.WorldNormal = _Object2World() o.WorldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.uv = v.texcoord * _RampTex_ST.xy + _RampTex_ST.zw; return o; } fixed4 frag(v2f i):SV_TARGET0{ fixed3 worldNormal = normalize(i.WorldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed halfLambert = 0.5 * dot(worldNormal,worldLightDir) + 0.5; fixed3 diffuseColor = tex2D(_RampTex,fixed2(halfLambert,halfLambert)).rgb * _Color.rgb; fixed3 diffuse = _LightColor0 * diffuseColor; fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss); return fixed4(ambient + diffuse + specular , 1.0); } ENDCG } } FallBack "Diffuse" }
- 遮罩纹理[控制高光反射,在高光反射的时候加进纹理采样得到的数据
总结:这几种映射,给我感觉是通过纹理采样,得到纹理的数据,对不同的光照种类进行调整,如果系数为0,就可以保证表现不受某种属性的影响。。 感觉还没理解透