unity-shader-07
2024年3月9日
摘要:ColAmbient、3ColAmbient、shadow、 光照模型组合
ColAmbient
环境光加上环境光遮蔽 - PS:AO叠加个颜色
3ColAmbient
这应该叫三阶环境光,取Normal的G通道-在分别获取mask,然后与AO混合。
改进-下方是基于取Normal的GB通道进行add-取mask,然后与ao混合,在美术上就是与ao的实际灰阶进行混合。
3ColAmbient VScood
步骤:
- 以手写Lambert (第3课)作为模板,复制粘贴大法;
- 修改Shader路径名;
- 定义面板参数:
- 贴图参数的定义方法:
_XXX (“面板标签”, 2d) = “white” {}
“white”{}
代表缺省纹理为纯白贴图,其他还有“black”{}
“gray”{}
…
- 贴图参数的定义方法:
- 对应的声明输入参数;
- 输入结构追加uv0;
- 输出结构追加uv;
- 顶点Shader中对uv做赋值;
- 准备向量 nDir;
- 通过nDir计算朝上,朝下,侧面各部位遮罩;
- 通过部位遮罩混合最终环境光颜色;
- 采样Occlusion图,获得环境遮挡信息;采样贴图方法:
tex2D(_Texture, uv);
- 用环境遮挡信息对环境光做遮挡;
- 输出最终颜色。
Code-view
L07_3ColAmbient_VS.shader
Shader "AP1/L07/L07_3ColAmbient_Vs" {
// 材质面板参数
Properties {
//暴露参数
_Occlusion ("Occlusion", 2d) = "white"{}
_EnvUpCol ("EnvUpCol", Color) = (1,1,1,1)
_EnvSideCol ("EnvSideCol", Color) = (0.5,0.5,0.5,1)
_EnvDownCol ("EnvDownCol", Color) = (0,0,0,1)
}
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
// 输入参数
uniform sampler2D _Occlusion;
uniform float3 _EnvUpCol;
uniform float3 _EnvSideCol;
uniform float3 _EnvDownCol;
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 将模型顶点信息输入进来
float4 normal : NORMAL; // 将模型法线信息输入进来
float2 uv0 : TEXCOORD0; // 将模型UV信息输入进来 0通道 共4通道
};
// 输出结构
struct VertexOutput {
float4 pos : SV_POSITION; // 由模型顶点信息换算而来的顶点屏幕位置
float3 nDirWS : TEXCOORD0; // 由模型法线信息换算来的世界空间法线信息
float2 uv : TEXCOORD1; // 追加UV信息用语像素Shader采样贴图
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos( v.vertex ); // 变换顶点信息 并将其塞给输出结构
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线信息 并将其塞给输出结构
o.uv = v.uv0; // 采集输出贴图
return o; // 将输出结构 输出
}
// 输出结构 >>> 像素
float4 frag(VertexOutput i) : COLOR {
//准备所有要用到的向量
float3 nDir = i.nDirWS; // 获取nDir
float upMask =max(0.0, nDir.g); // 获取朝上部分遮罩
float downMask =max(0.0, -nDir.g); // 获取朝下部分遮罩
float sideMask =1.0 - upMask - downMask; // 获取侧面部分遮罩
float3 envCol = (_EnvUpCol * upMask ) + (_EnvSideCol * sideMask) + (_EnvDownCol * downMask); // 混合环境色
float occlusion = tex2D (_Occlusion , i.uv); // 采样Occlusion贴图
float3 envLighting = envCol * occlusion; // 计算环境光照
return float4(envLighting , 1.0); // 返回最终颜色
}
ENDCG
}
}
FallBack "Diffuse"
}
// ps:要主要能复制就复制 不要手打
shadow VScood
unity 有自带封装好的投影代码
步骤:
- 以手写FlatCol(第3课)作为模板,复制粘贴大法;
- 修改Shader路径名;
- 无需面板参数;
- 无需声明输入参数;
- 无需修改输入结构;
- 输出结构追加:
LIGHTING_COORDS(0, 1):
- 此为Unity封装好的输出结构内容,照写就行,暂时不看细节;
- 括号中的参入,如(0, 1);0,1分别代表占用了TEXCOORD1和TEXCOORD2;
- 顶点Shader中必须调用Unity封装好的方法:
TRANSFER_VERTEX_TO_FRAGMENT(o);
- 像素Shader中获取投影信息同样通过Unity提供的方法:
LIGHT_ATTENUATION(i);
- 将投影作为结果输出。
Code-view - 叠加了个Lambert
L07_shadow_VS.shader
Shader "AP1/L07/L07_shadow_vs" // 自定义Shader路径
{
// 材质面板参数
Properties {
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "AutoLight.cginc" // 使用Unity投影必须包含这两个库文件
#include "Lighting.cginc" // 同上
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform float _SwitchLambert;
// 输入结构
struct VertexInput {
float4 vertex : POSITION; //将模型的顶点信息输入进来
float3 normal : NORMAL; //将模型的noraml信息输入进来
};
// 输出结构
struct VertexOutput {
float4 pos : SV_POSITION; //由模型顶点信息换算而来的顶点屏幕位置
float3 nDirWS : TEXCOORD1; //由模型法线信息换算来的世界空间法线信息
LIGHTING_COORDS(2,3) //投影用坐标信息 Unity已封装 不用管细节 , 这里COORDS(2,3) 是根据已有的TEXCOORDn坐标添加排列。 例如TEXCOORD1上面有1了那么继续往下排列。
//TEXCOORD是指纹理坐标,float2, float3, float4类型。n是指第几组纹理坐标,maya用户可以理解为UV集
};
// 输入结构 >>> 顶点shader >>> 输出结构 ,下面是个函数构架
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos( v.vertex ); // 变换顶点信息 并将其塞给输出结构
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线信息 并将其塞给输出结构
TRANSFER_VERTEX_TO_FRAGMENT(o) // Unity封装 不用管细节
return o; // 将输出结构 输出
}
// 输出结构 >>> 像素
float4 frag(VertexOutput i) : COLOR {
float3 nDir = i.nDirWS; // 获取nDir
float3 lDir = normalize(_WorldSpaceLightPos0.xyz); // 获取lDir
float nDotl = dot(nDir, lDir); // nDir点积lDir
float lambert = max(0.0, nDotl); // 截断负值
// return float4 (lambert, lambert, lambert, 1.0); // 输出最终颜色
float shadow = LIGHT_ATTENUATION(i); // 同样Unity封装好的函数 可取出投影
float3 lambertShadow = (lambert * shadow) ; // 多乘了个Lambert
return float4 ( lambertShadow , 1.0); // 输出最终颜色
}
ENDCG
}
}
FallBack "Diffuse"
}
简化理解光照构成-光照模型组合
将之前的内容结合
在这里环境的遮挡也可以使用后处理,对整个场景的环境遮蔽进行叠加。
OldSchoolPlus
作业:
OldSchoolPlus-vs
Code-view
L07_OldSchoolPlus_VS.shader
Shader "AP1/L07/L07_OldSchoolPlus_VS" {
// 材质面板参数
Properties {
//暴露参数
_MainCol ("baseCol", color) = (1.0, 1.0, 1.0, 1.0)
_MainCol2 ("highLight", color) = (1.0, 1.0, 1.0, 1.0)
_SpecularPow ("SpecularPow", range(1,50)) = 30
_ShadowPow ("ShadowPow", range(1,50)) = 10
_Occlusion ("AoTex", 2d) = "white"{}
_EnvUpCol ("EnvUpCol", Color) = (1,1,1,1)
_EnvSideCol ("EnvSideCol", Color) = (0.5,0.5,0.5,1)
_EnvDownCol ("EnvDownCol", Color) = (0,0,0,1)
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "AutoLight.cginc" // Unity投影库文件
#include "Lighting.cginc" // Unity投影库文件
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform float3 _MainCol2; // RGB float3
uniform float3 _MainCol; // RGB float3
uniform float _SpecularPow; // 标量 float
uniform sampler2D _Occlusion;
uniform float3 _EnvUpCol;
uniform float3 _EnvSideCol;
uniform float3 _EnvDownCol;
uniform float _ShadowPow ; // add shadow Pow sider
// 输入结构
struct VertexInput {
float4 vertex : POSITION; //将模型的顶点信息输入进来
float3 normal : NORMAL; //将模型的noraml信息输入进来
float2 uv0 : TEXCOORD0; // 将模型UV信息输入进来 0通道 共4通道
};
// 输出结构
struct VertexOutput {
float4 pos : SV_POSITION; //裁剪空间(暂理解为屏幕空间吧)顶点位置- 由模型顶点信息换算而来的顶点屏幕位置
float4 posWS : TEXCOORD0; //世界空间顶点位置
float3 nDirWS : TEXCOORD1; //世界空间法线方向-由模型法线信息换算来的世界空间法线信息
LIGHTING_COORDS(2,3) //投影用坐标信息
float2 uv : TEXCOORD4; // 追加UV信息用语像素Shader采样贴图
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos( v.vertex ); // 变换顶点位置 OS>CS -变换顶点信息 并将其塞给输出结构
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 变换顶点位置 OS>WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线方向 OS>WS -变换法线信息 并将其塞给输出结构
TRANSFER_VERTEX_TO_FRAGMENT(o) // 投影用Unity封装
o.uv = v.uv0; // 采集输出贴图
return o; // 将输出结构 输出
}
// 输出结构 >>> 像素
float4 frag(VertexOutput i) : COLOR {
//准备所有要用到的向量
float3 nDir = i.nDirWS;
float3 lDir = _WorldSpaceLightPos0.xyz;
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
float shadow = LIGHT_ATTENUATION(i); // 同样Unity封装好的函数 可取出投影
float3 vRDir = reflect( -vDir, nDir );
//准备所有要用到的中间数据,这里是两个点积结果;
float nDotl = dot(nDir, lDir); //得到lambert
float vRDotl = dot(vRDir, lDir); //得到phong
//编写光照模型
float lambert = max(0.0, nDotl);
float Phong = pow(max(0.0,vRDotl),_SpecularPow);
float3 LambertPhong = (_MainCol* lambert) + (Phong * _MainCol2);
//shadow
float shadowPow = pow(shadow , _ShadowPow) ; //阴影硬度
float3 shadowPhong = LambertPhong * shadowPow;
//3ColAmbient
float2 nDirgbadd = (nDir.g+nDir.b); // 取GB的mask
float upMask =max(0.0,nDirgbadd); // 获取朝上部分遮罩
float downMask =max(0.0, -nDirgbadd); // 获取朝下部分遮罩
float sideMask =1.0 - upMask - downMask; // 获取侧面部分遮罩
float3 envCol = (_EnvUpCol * upMask ) + (_EnvSideCol * sideMask) + (_EnvDownCol * downMask); // 混合环境色
float occlusion = tex2D (_Occlusion , i.uv); // 采样Occlusion贴图
float3 envLighting = envCol * occlusion; // 计算环境光照
float3 finalCol = envLighting+shadowPhong;
//返回结果
return float4(finalCol, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}