shader入门
类型
内置文件
三种精度
光照模型
标准光照模型:
只关心直接光照,光源发出的光线经过物体表面的一次反射直接进入相机的光线。
方法:
把进入相机的光线分为四个部分,每个部分用一种方法计算贡献度:
-
自发光emissive:等于物体材质的自发光颜色
-
高光反射specular: 需要知道法线,视角方向,光源方向,反射方向
Phong模型:
Blinn模型:
避免计算反射方向,将入射方向和光源方向去平均值并归一。然后用法线和新得到的向量去计算:
-
漫反射diffuse:【兰伯特定律:反射强度和法线与光源方向夹角的余弦值成正比】,注意去掉背面的光,即余弦值小于0
这样光找不到的地方全是黑的,可以增加环境光来变亮,但是明暗程度还是一样的。出现了
【半兰伯特光照模型:就是将原来兰伯特模型限制背光为0变为数值映射,即[-1,1]->[0,1] 在原模型的点积后*0.5再加0.5】 -
环境光ambient:等于设置的一个固定环境光
半兰伯特光照模型
高光反射光照模型(Phong光照模型):
高光反射是视线方向和反射方向的夹角余弦值
高光反射中反射方向计算:
高光反射光照模型(Blinn-Phong光照模型):
不关心反射光,将视角方向和入射光方向相加,然后归一化。
计算光照模型
逐像素光照:
逐像素计算漫反射光(兰伯特光照模型)
Shader "Mytest/DiffuseLightFragShader"
{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct v2f{
float4 pos: SV_POSITION;
//float3 color:COLOR;
float3 worldNormal:TEXCOORD0;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
///漫反射光
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
o.worldNormal = normalize(normalWorld);
return o;
}
fixed4 frag(v2f i):SV_Target{
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 兰伯特定律
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *saturate(dot(LightWorld,i.worldNormal));
//光
fixed3 color = diffuseLight+ambient;
return fixed4(color,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素计算漫反射光(半兰伯特光照模型)
Shader "Mytest/DiffuseLightFragShader_HalfLambert"
{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct v2f{
float4 pos: SV_POSITION;
//float3 color:COLOR;
float3 worldNormal:TEXCOORD0;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
///漫反射光
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
o.worldNormal = normalize(normalWorld);
return o;
}
fixed4 frag(v2f i):SV_Target{
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 半兰伯特定律
float halfLMBT = dot(LightWorld,i.worldNormal)*0.5+0.5;
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *halfLMBT;
//光
fixed3 color = diffuseLight+ambient;
return fixed4(color,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素计算高光反射(Phong模型)
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Mytest/SpecularLightFragShader"{
Properties{
//漫反射颜色
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
//高光颜色
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
//高光大小
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos: SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
o.worldNormal = normalize(normalWorld);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target{
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
///漫反射光
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 兰伯特定律
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *saturate(dot(LightWorld,i.worldNormal));
///高光反射
//根据公式需要知道反射光方向和视角方向
//反射光方向 并归一
fixed3 reflectDir = normalize(reflect(-LightWorld,i.worldNormal));
//视角方向 都放在世界坐标下,相机-点位 即视角方向
//获取视角方向,并归一化
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
//反射光
fixed3 specularLight = _LightColor0.rgb*_SpecularColor.rgb* pow(saturate(dot(reflectDir,viewDir)),_Gloss);
//光
fixed3 color = diffuseLight + ambient + specularLight;
return fixed4(color,1);
}
ENDCG
}
}
FallBack "Specular"
}
逐像素计算高光反射(Blinn-Phong模型)
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Mytest/SpecularLightFragShader_BlinnPhong"{
Properties{
//漫反射颜色
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
//高光颜色
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
//高光大小
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos: SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
o.worldNormal = normalize(normalWorld);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target{
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
///漫反射光
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 兰伯特定律
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *saturate(dot(LightWorld,i.worldNormal));
///高光反射
//根据公式需要知道 法线 和 视角和入射光的和向量
//视角方向 都放在世界坐标下,相机-点位 即视角方向
//获取视角方向,并归一化
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
float3 newDir =normalize( viewDir+LightWorld);
//反射光
//fixed3 specularLight = _LightColor0.rgb*_SpecularColor.rgb* pow(saturate(dot(reflectDir,viewDir)),_Gloss);
fixed3 specularLight = _LightColor0.rgb*_SpecularColor.rgb* pow(saturate(dot(newDir,i.worldNormal)),_Gloss);
//光
fixed3 color = diffuseLight + ambient + specularLight;
return fixed4(color,1);
}
ENDCG
}
}
FallBack "Specular"
}
逐顶点光照:
逐顶点计算漫反射光(兰伯特光照模型)
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Mytest/DiffuseLightShader"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct v2f{
float4 pos: SV_POSITION;
float3 color:COLOR;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
///漫反射光
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
float3 NNormalWorld = normalize(normalWorld);
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 兰伯特定律
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *saturate(dot(LightWorld,NNormalWorld));
//光
o.color = diffuseLight+ambient;
return o;
}
fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐顶点计算漫反射光(半兰伯特光照模型)
区别就只是half
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Mytest/DiffuseLightShader_HalfLambert"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct v2f{
float4 pos: SV_POSITION;
float3 color:COLOR;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
///漫反射光
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
float3 NNormalWorld = normalize(normalWorld);
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 半兰伯特定律
float halfLMBT = dot(LightWorld,NNormalWorld)*0.5 + 0.5;
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb * halfLMBT;
//光
o.color = diffuseLight+ambient;
return o;
}
fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐顶点计算高光反射(Phong模型)
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Mytest/SpecularLightShader"{
Properties{
//漫反射颜色
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
//高光颜色
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
//高光大小
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos: SV_POSITION;
fixed3 color:COLOR;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
///漫反射光
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
float3 NNormalWorld = normalize(normalWorld);
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 兰伯特定律
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *saturate(dot(LightWorld,NNormalWorld));
///高光反射
//根据公式需要知道反射光方向和视角方向
//反射光方向 并归一 此处reflect需要光源指向交点,所以取反
fixed3 reflectDir = normalize(reflect(-LightWorld,NNormalWorld));
//视角方向 都放在世界坐标下,相机-点位 即视角方向
//需要将点转移到世界坐标
fixed3 vertexWorldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//获取视角方向,并归一化
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-vertexWorldPos);
//反射光
float3 specularLight = _LightColor0.rgb*_SpecularColor.rgb* pow(saturate(dot(reflectDir,viewDir)),_Gloss);
//光
o.color = diffuseLight + ambient + specularLight;
return o;
}
fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1);
}
ENDCG
}
}
FallBack "Specular"
}
逐顶点计算高光反射(Blinn-Phong模型)
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Mytest/SpecularLightShader_BlinnPhong"{
Properties{
//漫反射颜色
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
//高光颜色
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
//高光大小
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos: SV_POSITION;
fixed3 color:COLOR;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
///环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
///漫反射光
//在世界坐标下进行
//对法线转换 需要用到 变换矩阵的 逆转置 矩阵
//_World2Object是Object2World的逆矩阵
//转置,就变成左乘向量
float3 normalWorld = mul(v.normal,(float3x3)unity_WorldToObject);
//归一化
float3 NNormalWorld = normalize(normalWorld);
//光照方向 (只适用一个方向光)
float3 LightWorld = normalize( _WorldSpaceLightPos0.xyz);
//计算
//法线和光照方向点乘 兰伯特定律
float3 diffuseLight = _DiffuseColor.rgb* _LightColor0.rgb *saturate(dot(LightWorld,NNormalWorld));
///高光反射
//根据公式需要知道反射光方向和视角方向
//反射光方向 并归一 此处reflect需要光源指向交点,所以取反
//fixed3 reflectDir = normalize(reflect(-LightWorld,NNormalWorld));
//视角方向 都放在世界坐标下,相机-点位 即视角方向
//需要将点转移到世界坐标
fixed3 vertexWorldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//获取视角方向,并归一化
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-vertexWorldPos);
//blinn模型需要的视角方向和入射光方向相加 再归一化
float3 newDir = normalize(viewDir + LightWorld);
float3 specularLight = _LightColor0.rgb*_SpecularColor.rgb* pow(saturate(dot(newDir,NNormalWorld)),_Gloss);
//反射光
//float3 specularLight = _LightColor0.rgb*_SpecularColor.rgb* pow(saturate(dot(reflectDir,viewDir)),_Gloss);
//光
o.color = diffuseLight + ambient + specularLight;
return o;
}
fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1);
}
ENDCG
}
}
FallBack "Specular"
}
上边光照计算都是基于只有一个平行光,其他光源计算方式不同。且上边计算高光反射时,使用的漫反射都是兰伯特模型。
告别基础运算,使用内置方法
计算光照模型常用函数:
纹理!
模型的色彩通过纹理映射来展现,纹理坐标又称UV坐标(U横,V纵)范围是[0,1],存储在顶点信息中.
unity纹理的一些属性:
Texture Type纹理类型
Wrap Mode 决定纹理坐标超过[0,1]范围后的值
Repeat:超出部分计算小数部分采样。即不断重复
Clamp:超出1的都是1,小于0的都是0
Filter Mode 纹理由于变换产生拉伸时将会采用那种滤波模式
point,bilinear,trilinear 效果依次提升,但性能耗费增加
mipmapping 多级渐远纹理技术
抗锯齿问题:提前处理纹理,生成很多小的图像,像金字塔一样,每一层都是对上一层降级采样。
需要耗费空间去存储 多占用33%内存 空间换时间
纹理的最大尺寸
为不同平台选择不同纹理尺寸
纹理长宽尽量为2的幂
NPOT(non power of two)非2的幂
纹理格式:
纹理格式精度越高,效果越好,占用和耗费越大
不需要高精度时,尽量用压缩格式的纹理,例如漫反射纹理
OpenGL和DirectX中差异
OpenGL:原点在左下角
DirectX:原点在左上角
unity使用OpenGL的在左下角,且它已经处理好不同平台的问题
纹理实践
在shader中,properties中定义的纹理名称+_ST 可以获取到纹理的缩放xy和平移zw值
基本采样(纹理的使用):
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Mytest/TextureBaseUseShader"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
_DiffuseTexture("DiffuseTex",2D) = "white"{}
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,256))=20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
sampler2D _DiffuseTexture;
//纹理名+_ST 获取该纹理的缩放xy和平移zw值
float4 _DiffuseTexture_ST;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal: TEXCOORD0;
float4 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//时刻记住,用变换矩阵的逆转置矩阵来获得法线, 否则法线方向会被拉伸时改变
o.worldNormal = normalize( UnityObjectToWorldNormal(v.normal));
o.worldPos = mul( unity_ObjectToWorld,v.vertex);
o.uv = v.texcoord.xy * _DiffuseTexture_ST.xy +_DiffuseTexture_ST.zw;
//内置函数
//o.uv = TRANSFORM_TEX(v.texcoord,_DiffuseTexture);
return o;
}
fixed4 frag(v2f i):SV_Target{
//入射光
fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(i.worldPos) );
//将纹理的对应坐标颜色和漫反射光颜色混合
fixed3 albedo = tex2D(_DiffuseTexture,i.uv).rgb * _DiffuseColor.rgb;
///这边跟书上不太一样,按自己理解写了
//环境光
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.xyz ;
//漫反射光 入射光和法线点积
fixed3 diffuseLight = albedo * _LightColor0.rgb * max(0,(dot(worldLightDir,i.worldNormal)));
//高光反射计算 入射光和视角方向的合 以及 法线
fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldPos) );
fixed3 NewDir = normalize( viewDir + worldLightDir);
fixed3 specularLight = _SpecularColor.rgb * _LightColor0.rgb * pow( max(0,dot(NewDir,i.worldNormal)),_Gloss );
fixed3 color = ambientLight + diffuseLight + specularLight;
return fixed4(color,1);
}
ENDCG
}
}
FallBack "Specular"
}
凹凸纹理(使用纹理表现凹凸效果):
原理:
使用一张纹理来修改法线,让模型看起来是凹凸的,但顶点位置不变,也就是轮廓是不变的。
两种方式:
- 高度映射:
使用一张高度纹理,模拟表面位移,来得到一个模拟有位移的法线值。
高度纹理:存储强度值,表示模型表面局部的高度,越浅越凸起,越暗越凹陷;
优点:直观,看以看出凹凸
缺点:计算复杂,需要根据灰度值计算,耗费性能。
- 法线映射:
使用一张法线纹理,直接存储着法线值。
法线纹理:存储表面法线方向。 法线范围[-1,1] 像素分量[0,1]
所以需要映射 +1 /2;
所以在计算时,需要反向映射,获取原来法线的真实值
因为存储的时单位向量,所以只需要xy两个值即可,z可以通过计算得到
模型空间的法线纹理:
切线空间的法线纹理:顶点的切线空间
在切线空间下计算法线:
Shader "Mytest/NormalInTangentSpaceShader"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
_DiffuseTexture("DiffuseTexture",2D) = "white"{}
_NormalMap("NormalMap",2D)="bump"{}
_BumpScale("BumpScale",Float)=1
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed3 _DiffuseColor;
sampler2D _DiffuseTexture;
float4 _DiffuseTexture_ST;
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _BumpScale;
fixed3 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float3 viewDir: TEXCOORD1;//切线空间下的视角方向
float3 lightDir:TEXCOORD2;//切线空间下的入射光方向
};
v2f vert(appdata_tan v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//纹理映射和法线纹理映射
o.uv.xy = v.texcoord.xy * _DiffuseTexture_ST.xy + _DiffuseTexture_ST.zw;
o.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;
//内置函数,切线空间
TANGENT_SPACE_ROTATION;
//#define TANGENT_SPACE_ROTATION \
//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_Target{
//切线空间下的入射光方向
fixed3 tangentLightDir = normalize(i.lightDir);
//切线空间下的视角方向
fixed3 tangentViewDir = normalize(i.viewDir);
//获取法线纹理数据
fixed4 getNormal = tex2D(_NormalMap,i.uv.zw);
//获取切线空间下的法线
fixed3 tangentNormal;
//图片未设置为法线贴图需要反向映射
//解除法线到颜色的映射关系得到真实法线 和自己加的法线系数相乘
//tangentNormal.xy = (getNormal * 2 - 1) * _BumpScale;
//tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));
//内置api
tangentNormal = UnpackNormal(getNormal);
tangentNormal.xy *=_BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));
//混合纹理颜色
fixed3 albedo = tex2D(_DiffuseTexture,i.uv.zy);
//光计算
//环境光
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射光
//新的法线和入射光
fixed3 diffuseLight = _DiffuseColor.rgb * albedo * _LightColor0.rgb * saturate( dot(tangentNormal,tangentLightDir));
//高光反射
//新的法线 和 入射光和视角合
float3 newDir = normalize(tangentLightDir + tangentViewDir);
fixed3 specularLight = _SpecularColor.rgb * _LightColor0.rgb * pow( saturate(dot(newDir,tangentNormal)),_Gloss);
fixed3 color = ambientLight + diffuseLight +specularLight;
return fixed4(color.rgb,1);
}
ENDCG
}
}
FallBack "Specular"
}
在世界空间下计算法线:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Mytest/NormalInWorldSpaceShader"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
_DiffuseTexture("DiffuseTexture",2D) = "white"{}
_NormalMap("NormalMap",2D) = "bump"{}
_BumpScale("BumpScale",Float) = 1
_SpecularColor("SpecularColor",Color)= (1,1,1,1)
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
sampler2D _DiffuseTexture;
float4 _DiffuseTexture_ST;
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _BumpScale;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos: SV_POSITION;
float4 uv:TEXCOORD0;
float4 TtoW0:TEXCOORD1;
float4 TtoW1:TEXCOORD2;
float4 TtoW2:TEXCOORD3;
};
v2f vert(appdata_tan v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//顶点的纹理信息
o.uv.xy = v.texcoord.xy * _DiffuseTexture_ST.xy + _DiffuseTexture_ST.zw;
o.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;
//获取世界坐标下数据
float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
//世界坐标下的切线
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
//副切线
fixed3 worldBinormal = cross(worldNormal,worldTangent) * v.tangent.w;
//获取切线空间转世界的矩阵 顺便把世界坐标塞进去,节省寄存器
o.TtoW0 = float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
o.TtoW1 = float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
o.TtoW2 = float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
return o;
}
fixed4 frag(v2f i):SV_Target{
//基础数据获得
//漫反射纹理采样
fixed3 albedo = tex2D(_DiffuseTexture,i.uv.zy);
float3 worldPos = normalize( float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w) );//世界位置
fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(worldPos));//入射光
fixed3 worldViewDir = normalize( UnityWorldSpaceViewDir(worldPos));
//法线纹理提取 先采样获取颜色,再解析颜色到法线
fixed3 normalMap = UnpackNormal( tex2D(_NormalMap,i.uv.zw) );
normalMap.xy *= _BumpScale;//法线 加上自己设置的深度缩放
normalMap.z = sqrt(1-max(0,dot(normalMap.xy,normalMap.xy)));//由xy计算z值
//法线从切线空间转换到世界空间
fixed3 worldNormal = normalize(half3(dot(i.TtoW0.xyz,normalMap),dot(i.TtoW1.xyz,normalMap),dot(i.TtoW2.xyz,normalMap)));
还是光计算 再来N遍!
//环境光
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射光 入射光和法线
fixed3 diffuseLight = _LightColor0.rgb * _DiffuseColor.rgb * albedo * max(0,dot(worldLightDir,worldNormal));
//高光反射 法线和 入射光和视角合
float3 newDir = normalize(worldLightDir + worldViewDir);
fixed3 specularLight = _LightColor0.rgb * _SpecularColor.rgb * pow( max(0,dot(newDir,worldNormal)) ,_Gloss);
//合
fixed3 color = ambientLight + diffuseLight + specularLight;
return fixed4(color.rgb,1);
}
ENDCG
}
}
FallBack "Specular"
}
渐变纹理:
一开始纹理只是为了定义颜色,但后来发现,纹理可以存储任何表面信息。
使用渐变纹理,控制漫反射光照效果
Shader "Mytest/RampShader"{
Properties{
_DiffuseColor("DiffuseColor",Color)=(1,1,1,1)
_RampMap("RampMap",2D)="white"{}
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
sampler2D _RampMap;
float4 _RampMap_ST;
fixed4 _SpecularColor;
float _Gloss;
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize( UnityObjectToWorldNormal(v.normal) );
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//内置函数 不用自己再写了:(tex.xy * name##_ST.xy + name##_ST.zw)
o.uv = TRANSFORM_TEX(v.texcoord,_RampMap);
return o;
}
fixed4 frag(v2f i):SV_Target{
//入射光
fixed3 worldLight = UnityWorldSpaceLightDir(i.worldPos);
//环境光
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
//半兰伯特
fixed halfLMBT = dot(worldLight,i.worldNormal) * 0.5 + 0.5;
fixed3 changeColor = tex2D(_RampMap, fixed2(halfLMBT,halfLMBT)).rgb;
fixed3 diffuseLight = _LightColor0.rgb * _DiffuseColor.rgb * changeColor;
//高光反射
fixed3 worldView = normalize( UnityWorldSpaceViewDir(i.worldPos) );
fixed3 newDir = normalize( worldView + worldLight);
fixed3 specularLight = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0,dot(newDir,i.worldNormal)),_Gloss);
return fixed4(ambientLight+diffuseLight+specularLight,1);
}
ENDCG
}
}
FallBack "Specular"
}
遮罩纹理:
提供一种表面数据,来控制其他数据的变化。
Shader "Mytest/MaskTextureShader"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
_SpecularColor("SpecularColor",Color)=(1,1,1,1)
_Gloss("Gloss",Range(8,256)) = 20
_DiffuseTexture("DiffuseTexture",2D)="white"{}
_BumpMap("BumpMap",2D)="bump"{}
_BumpScale("BumpScale",Float)=1
_MaskMap("MaskMap",2D)="white"{}
_MaskScale("MaskScale",Float)=1
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed3 _DiffuseColor;
fixed3 _SpecularColor;
float _Gloss;
sampler2D _MaskMap;//遮罩纹理
sampler2D _DiffuseTexture;
float4 _DiffuseTexture_ST;
sampler2D _BumpMap;//法线纹理
float _BumpScale;//法线纹理控制影响程度
float _MaskScale;//遮罩纹理控制影响程度
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
//fixed3 worldNormal:TEXCOORD1;
float3 lightDir:TEXCOORD1;
float3 viewDir:TEXCOORD2;
};
v2f vert(appdata_tan v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _DiffuseTexture_ST.xy + _DiffuseTexture_ST.zw;
TANGENT_SPACE_ROTATION;
o.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
o.viewDir = mul(rotation,ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target{
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed3 normalMapData = UnpackNormal( tex2D(_BumpMap,i.uv));
normalMapData.xy *= _BumpScale;
normalMapData.z = sqrt(1-max(0,dot(normalMapData.xy,normalMapData.xy)));
//贴图纹理
fixed3 albedo = tex2D(_DiffuseTexture,i.uv).rgb * _DiffuseColor.rgb;
//环境光
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;
//漫反射光
fixed3 diffuseLight = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir,normalMapData));
//高光反射
fixed3 newDir = normalize(tangentLightDir + tangentViewDir);
//遮罩纹理采样
float maskData = tex2D(_MaskMap,i.uv).r * _MaskScale;
fixed3 specularLight = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0,dot(newDir,normalMapData)),_Gloss) * maskData;
return fixed4(ambientLight + diffuseLight + specularLight,1);
}
ENDCG
}
}
FallBack "Specular"
}
透明
深度缓存(Z-Buffer)
解决物体可见性问题,物体的遮挡关系;
透明度测试(Alpha Test)
Shader "Mytest/AlphaTestShader"{
Properties{
_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
_DiffuseTexture("DiffuseTexture",2D)="white"{}
_CutOff("cutoff",Range(0,1))=0.5
}
SubShader{
Tags{"Quene"="AlphaTest"
"IgnoreProjector"="True"
"RenderType"="TransparentCutout"
}
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed3 _DiffuseColor;
sampler2D _DiffuseTexture;
float4 _DiffuseTexture_ST;
float _CutOff;
struct v2f{
float4 pos:SV_POSITION;
float3 worldPos:TEXCOORD0;
fixed3 worldNormal:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize( UnityObjectToWorldNormal(v.normal));
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord,_DiffuseTexture);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 worldLight = normalize( UnityWorldSpaceLightDir(i.worldPos) );
//fixed3 viewLight = normalize( UnityWorldSpaceViewDir(i.worldPos) );
//
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed4 textureColor = tex2D(_DiffuseTexture,i.uv);
//Alpha Test
clip(textureColor.a - _CutOff);
if((textureColor.a - _CutOff)<0.0){
discard;
}
//
fixed3 diffuseLight = _LightColor0.rgb * _DiffuseColor.rgb * textureColor.rgb * max(0,dot(i.worldNormal,worldLight));
//
//fixed3 newDir = normalize( worldLight + )
return fixed4(ambientLight + diffuseLight,1);
}
ENDCG
}
}
FallBack ""
}
透明度混合(Alpha Blending)
需要关闭深度写入
将当前像素作为混合因子和深度缓存里的像素做混合,混合方式可以配置
Shader "Mytest/AlphaBlendShader"{
Properties{
_DiffuseColor("DiffuseColor",Color)=(1,1,1,1)
_DiffuseTexture("DiffuseTexture",2D)="white"{}
_AlphaScale("AlphaScale",Range(0,1)) = 1
}
SubShader{
Tags{"Quene"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
sampler2D _DiffuseTexture;
float4 _DiffuseTexture_ST;
float _AlphaScale;
struct v2f{
float4 pos:SV_POSITION;//顶点的剪裁空间坐标
float3 worldPos:TEXCOORD0;//世界空间下的顶点
fixed3 worldNormal:TEXCOORD1;//世界空间下的法线 归一化后
float2 uv:TEXCOORD2;//顶点UV坐标
};
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
o.worldNormal = normalize( UnityObjectToWorldNormal(v.normal) );
o.uv.xy = v.texcoord.xy * _DiffuseTexture_ST.xy + _DiffuseTexture_ST.zw;
return o;
}
fixed4 frag(v2f i):SV_Target{
fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(i.worldPos) );
fixed4 textureColor = tex2D(_DiffuseTexture,i.uv);
//
fixed3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.rgb;
//漫反射 入射光和法线
fixed3 diffuseColor = _LightColor0.rgb * _DiffuseColor.rgb * textureColor.rgb * max(0,dot(i.worldNormal,worldLightDir));
return fixed4(ambientColor + diffuseColor,textureColor.a* _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
开启深度写入的半透明效果
使用两个pass来渲染模型,第一个Pass开启深度写入,不输出颜色,只是为了把模型的深度值写入深度缓存;第二个Pass进行透明混合,上一个pass得到的深度信息,在这个pass中按照像素深度信息来进行混合。
Shader "Mytest/AlphaBlendUseZWriteShader"{
Properties{
_DiffuseColor("DiffuseColor",Color)=(1,1,1,1)
_DiffuseTexture("DiffuseTexture",2D)="white"{}
_AlphaScale("AlphaScale",Range(0,1)) = 1
}
SubShader{
Tags{"Quene"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass{
ZWrite On
ColorMask 0//不写入任何颜色
}
Pass{
Tags{"LightMode"="ForwardBase"}
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
sampler _DiffuseTexture;
float4 _DiffuseTexture_ST;
float _AlphaScale;
struct v2f{
float4 pos:SV_POSITION;
fixed3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize( UnityObjectToWorldNormal(v.normal) );
o.worldPos = mul(unity_ObjectToWorld,v.vertex ).xyz;
o.uv.xy = TRANSFORM_TEX(v.texcoord,_DiffuseTexture);
return o;
}
fixed4 frag(v2f i):SV_Target{
//
fixed4 textureColor = tex2D(_DiffuseTexture,i.uv);
//入射光
fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(i.worldPos) );
fixed3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 diffuseLight = _LightColor0.rgb * _DiffuseColor.rgb * textureColor.rgb * max(0,dot(worldLightDir,i.worldNormal));
return fixed4(ambientLight + diffuseLight,_AlphaScale * textureColor.a);
}
ENDCG
}
}
Fallback ""
}
混合
双面透明效果:
Cull 剔除某个面的渲染
Cull Back | Front | Off
剔除背面,前面,关闭剔除
透明度测试 直接关闭剔除
透明度混合 通过使用两个Pass 第一个关闭前面渲染 第二个关闭后面渲染 先渲染背面,再渲染前面