“顶点点亮”路径通常一次渲染每个对象,并使用为每个顶点计算的所有光源的光照。
这是最快的渲染路径,并且具有最广泛的硬件支持。
由于所有照明都是在顶点级别计算的,因此此渲染路径不支持大多数每个像素的效果:不支持阴影,法线贴图,光cookie和高度详细的镜面高光。
UnityCG.cginc相关内置变量:
1、 float4 _WorldSpaceLightPos0
光源点(当w值为0时,属于平行光向量,否则为光源位置点,w值的意义暂且不知)
2、float4 unity_4LightPosX0、float4 unity_4LightPosY0、float4 unity_4LightPosZ0
代表4个光源(暂且不知是何种类型光源)的X,Y,Z分量。
对应还有float4 unity_4LightAtten0 对应4个光源的衰减值,float4 unity_LightColor[4] 对应光源的颜色
3、float4 unity_LightPosition[4] 代表4个点光源的位置,当w为0时代表是平行光。
对应还有float4 unity_LightAtten[4] 代表相应的衰减值,float4 unity_LightColor[4] 对应的颜色
注意:(2)中所说的内置变量在摄像机的RenderPath为VertexLit时,且Pass的LightMode为Vertex时不起作用,它们的值是上一次的残留值,不会发生实时改变,而其他任意情况都生效。
--------------------------2020年5月2日10:05:06更新---------------------------
在《Shader入门精要》的解释顶点照明路径是可以访问到8个逐顶点光源的,如果只有2个那么其余6个都会设置成默认的,如颜色为黑色,其内置变量和相关内置方法如下表。
名称 | 类型 | 描述 |
unity_LightColor | half4[8] | 光源颜色 |
unity_LightPosition | float4[8] | xyz分量是视角空间中的光源位置,如果光源是平行光,那么w分量值为0,其他光源类型w为1 |
unity_LightAtten | half4[8] | 光源衰减因子,如果光源是聚光灯,x分量是cos(spotAngle/2),y是1/cos(spotAngle/4); 如果是其他类型的光源,x分量是-1,y分量是1,z分量是衰减的平方,w分量是光源范围开根号的结果 |
unity_SpotDirection | float4[8] | 如果光源是聚光灯,值为视角空间的聚光灯位置;如果是其他类型的光源,值为(0,0,1,0) |
函数名 | 描述 |
float3 ShadeVertexLights(float4 vertex, float3 normal) | 输入模型空间的顶点位置和法线,计算四个逐顶点光源的光照以及环境光,返回的是一个color.rgb。内部调用的是ShadeVertexLightsFull方法来实现 |
float3 ShadeVertexLightsFull(float4 vertex, float3 normal, int lightCount, bool spotLight) | 输入模块空间的顶点位置和法线,计算lightCount个光源的光照以及环境光,返回的是一个color.rgb,如果spotLight值为true,这些光源会当做聚光灯来处理,虽然结果会更准确,但计算更加耗时;否则,按点光源处理。 |
Vertex Pass部分应用
Shader "Unlit/VertexRenderPathTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags{"LightMode" = "Vertex"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 color : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.color = ShadeVertexLights(v.vertex, v.normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = col.rgb * i.color * 2;
return col;
}
ENDCG
}
}
FallBack "Diffuse"
}
UnityCG.cginc环境中的ShadeVertexLights方法如下所示。
float3 ShadeVertexLights (float4 vertex, float3 normal)
{
return ShadeVertexLightsFull (vertex, normal, 4, false);
}
// Used in Vertex pass: Calculates diffuse lighting from lightCount lights. Specifying true to spotLight is more expensive
// to calculate but lights are treated as spot lights otherwise they are treated as point lights.
float3 ShadeVertexLightsFull (float4 vertex, float3 normal, int lightCount, bool spotLight)
{
float3 viewpos = UnityObjectToViewPos (vertex);
float3 viewN = normalize (mul ((float3x3)UNITY_MATRIX_IT_MV, normal));
float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
for (int i = 0; i < lightCount; i++) {
float3 toLight = unity_LightPosition[i].xyz - viewpos.xyz * unity_LightPosition[i].w;
float lengthSq = dot(toLight, toLight);
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
toLight *= rsqrt(lengthSq);
float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
if (spotLight)
{
float rho = max (0, dot(toLight, unity_SpotDirection[i].xyz));
float spotAtt = (rho - unity_LightAtten[i].x) * unity_LightAtten[i].y;
atten *= saturate(spotAtt);
}
float diff = max (0, dot (viewN, toLight));
lightColor += unity_LightColor[i].rgb * (diff * atten);
}
return lightColor;
}
可见,ShadeVertexLights就是计算了4个逐顶点光源以及环境光的影响,返回了一个颜色纸rgb,然后我对它进行了混合操作。下面是我新增4个逐顶点的点光源的效果图。
这张颜色比较丰富的图就是我去除平行光之后的渲染效果图,可看出平行光被当作逐顶点光源处理。
其他内置变量的相关测试自己动手做吧,到此为止结束咯
---------------------------更新2020年5月2日21:15:36-----------------------------------
Shader "Unlit/VertexRenderPathPositionShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
//模式一(_VERTEXMODEL_ONE)有效,代表哪一个灯光影响物体
[Enum(ZeroLight,0,FirstLight,1,SecondLight,2)] _LightNum("LightNumber", Float) = 1
//分别为四个模式:其实也可以不用变体的形式来控制
//模式一: 可控制某一个灯光影响物体,只限一个;可控制环境光影响,通过下面的ENABLE_AMBIENT控制
//模式二:Shade4PointLights函数,受到环境光、前四个光源的影响(必然包含平行光)且不可控
//模式三:Shade4PointLightsFull函数,可控制数量和是否对SpotLight特殊处理(分别是函数的第三和第四个参数)
//模式四: 可空灯光影响个数、是否受环境光影响、是否受平行光影响、是否对SpotLight特殊处理,如有需其他需求自行修改
[KeywordEnum(One,Two,Three,Four)] _VertexModel("VertexModel", Float) = 0
//在VertexModel_One和VertexModel_Four模式下有效:是否开启环境光影响
[Toggle(ENABLE_AMBIENT)] _EnableAmbient("EnableAmbient", Float) = 0
//在VertexModel_Four模式下有效:光照个数
_LightCount("LightModelFour_CustomLightCount", Float) = 0
//在VertexModel_Four模式下有效:是否开启SpotLight计算效果(变化不大)
[Toggle] _EnableSotLight("EnableSpotLight", Float) = 0
//在VertexModel_Four模式下有效:是否忽略平行光影响
[Toggle] _IgnoreDirectionalLight("IgnoreDirectionalLight", Float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags{"LightMode" = "Vertex"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _VERTEXMODEL_ONE _VERTEXMODEL_TWO _VERTEXMODEL_THREE _VERTEXMODEL_FOUR
#pragma multi_compile _ ENABLE_AMBIENT
#pragma multi_compile _ ENABLE_SPOT_LIGHT
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 color : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _LightNum;
float _LightCount;
float _EnableSotLight;
float _IgnoreDirectionalLight;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
#if _VERTEXMODEL_ONE
//获取其中第_LightNum个光源位置 (视角空间)
float4 lightPos1 = unity_LightPosition[_LightNum];
fixed3 viewLightDir;
if (lightPos1.w == 0)
{
viewLightDir = lightPos1.xyz;
}
else {
viewLightDir = lightPos1.xyz - UnityObjectToViewPos(v.vertex).xyz * unity_LightPosition[0].w;
}
fixed3 viewNormal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
float lengthSq = dot(viewLightDir, viewLightDir);
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
viewLightDir *= rsqrt(lengthSq);
float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[_LightNum].z);
#if ENABLE_AMBIENT
//1.1环境光参与光照
o.color.rgb = UNITY_LIGHTMODEL_AMBIENT.xyz + unity_LightColor[_LightNum].rgb * max(0,dot(viewNormal, normalize(viewLightDir)));
#else
//1.2环境光不参与
o.color.rgb = unity_LightColor[_LightNum].rgb * max(0, dot(viewNormal, normalize(viewLightDir)));
#endif
#elif _VERTEXMODEL_TWO
o.color = ShadeVertexLights(v.vertex, v.normal); //函数为固定前4个光源的影响
#elif _VERTEXMODEL_THREE
o.color = ShadeVertexLightsFull(v.vertex, v.normal, 8, false);
#elif _VERTEXMODEL_FOUR
float3 viewpos = UnityObjectToViewPos(v.vertex);
float3 viewN = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
#if ENABLE_AMBIENT
float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
#else
float3 lightColor;
#endif
int startIndex = 0;
if (_IgnoreDirectionalLight) {
startIndex = 1;
}
for (int i = startIndex; i < _LightCount; i++) {
float3 toLight = unity_LightPosition[i].xyz - viewpos.xyz * unity_LightPosition[i].w;
float lengthSq = dot(toLight, toLight);
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
toLight *= rsqrt(lengthSq);
float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
if (_EnableSotLight)
{
float rho = max(0, dot(toLight, unity_SpotDirection[i].xyz));
float spotAtt = (rho - unity_LightAtten[i].x) * unity_LightAtten[i].y;
atten *= saturate(spotAtt);
}
float diff = max(0, dot(viewN, toLight));
lightColor += unity_LightColor[i].rgb * (diff * atten);
}
o.color = lightColor;
#endif
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = col.rgb * i.color * 2;
return col;
}
ENDCG
}
}
FallBack "Diffuse"
}
关于unity_LightXxxx[8] 这些灯光的顺序问题,貌似是根据灯光与物体的距离、灯光强度、模式等信息来调整顺序的。
平行光的Atten是1,所以平行光的影响是最大,如果没禁用平行光的话 其他灯光的影响可能会显得很小。例如下图:
甚至是直接覆盖了的情况。。
VertexLM、VertexLMRGBM
// Upgrade NOTE: commented out 'float4 unity_LightmapST', a built-in variable
// Upgrade NOTE: commented out 'sampler2D unity_Lightmap', a built-in variable
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
// Upgrade NOTE: replaced tex2D unity_Lightmap with UNITY_SAMPLE_TEX2D
Shader "VertexLit CG" {
Properties{
_Color("Main Color", Color) = (1,1,1,1)
_SpecColor("Spec Color", Color) = (1,1,1,0)
_Emission("Emissive Color", Color) = (0,0,0,0)
_Shininess("Shininess", Range(0.1, 1)) = 0.7
_MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader{
Tags {"Queue" = "Geometry" "IgnoreProjector" = "True"}
LOD 100
ZWrite On
//Lightmap pass, dLDR;
Pass {
Tags { "LightMode" = "VertexLM" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// float4 unity_LightmapST;
// sampler2D unity_Lightmap;
struct v2f {
float4 pos : SV_POSITION;
float2 lmap : TEXCOORD0;
};
v2f vert(appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
return o;
}
fixed4 frag(v2f i) : COLOR {
fixed4 lmtex = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lmap.xy);
fixed3 lm = 2.0 * lmtex.rgb;
return fixed4(lm, 1);
}
ENDCG
}
//Lightmap pass, RGBM;
Pass {
Tags { "LightMode" = "VertexLMRGBM" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// float4 unity_LightmapST;
// sampler2D unity_Lightmap;
struct v2f {
float4 pos : SV_POSITION;
float2 lmap : TEXCOORD0;
};
v2f vert(appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
return o;
}
fixed4 frag(v2f i) : COLOR {
fixed4 lmtex = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lmap.xy);
fixed3 lm = (8.0 * lmtex.a) * lmtex.rgb;
return fixed4(lm, 1);
}
ENDCG
}
}
//Fallback "VertexLit"
}
由于顶点照明最常用于不支持可编程着色器的平台,Unity 无法在内部创建多个着色器变体来处理光照映射与非光照映射的情况。 因此,为了处理光映射和非光映射对象,必须显式地编写多个Pass。
Vertex 针对固定渲染管线OpenGL/Direct3D光照模式 (Blinn-Phong),针对顶点进行渲染实时光照部分,片元会插值处理顶点颜色
VertexLM 针对移动平台,针对顶点进行渲染光照贴图(double-LDR编码),而不是渲染实时光,一般会将光照颜色与自身纹理混合。
VertexLMRGBM 针对PC和控制台,针对顶点进行渲染光照贴图(RGBM编码),而不是渲染实时光,一般会将光照颜色与自身纹理混合。
在UnityCG.cginc里拥有DecodeLightmap(fixed4 color)函数以及DecodeRealtimeLightmap( fixed4 color )具体如下:
// Decodes HDR textures
// handles dLDR, RGBM formats
// Called by DecodeLightmap when UNITY_NO_RGBM is not defined.
inline half3 DecodeLightmapRGBM (half4 data, half4 decodeInstructions)
{
// If Linear mode is not supported we can skip exponent part
#if defined(UNITY_COLORSPACE_GAMMA)
# if defined(UNITY_FORCE_LINEAR_READ_FOR_RGBM)
return (decodeInstructions.x * data.a) * sqrt(data.rgb);
# else
return (decodeInstructions.x * data.a) * data.rgb;
# endif
#else
return (decodeInstructions.x * pow(data.a, decodeInstructions.y)) * data.rgb;
#endif
}
// Decodes doubleLDR encoded lightmaps.
inline half3 DecodeLightmapDoubleLDR( fixed4 color )
{
return 2.0 * color.rgb;
}
inline half3 DecodeLightmap( fixed4 color, half4 decodeInstructions)
{
#if defined(UNITY_NO_RGBM)
return DecodeLightmapDoubleLDR( color );
#else
return DecodeLightmapRGBM( color, decodeInstructions );
#endif
}
half4 unity_Lightmap_HDR;
inline half3 DecodeLightmap( fixed4 color )
{
return DecodeLightmap( color, unity_Lightmap_HDR );
}
half4 unity_DynamicLightmap_HDR;
// Decodes Enlighten RGBM encoded lightmaps
// NOTE: Enlighten dynamic texture RGBM format is _different_ from standard Unity HDR textures
// (such as Baked Lightmaps, Reflection Probes and IBL images)
// Instead Enlighten provides RGBM texture in _Linear_ color space with _different_ exponent.
// WARNING: 3 pow operations, might be very expensive for mobiles!
inline half3 DecodeRealtimeLightmap( fixed4 color )
{
//@TODO: Temporary until Geomerics gives us an API to convert lightmaps to RGBM in gamma space on the enlighten thread before we upload the textures.
#if defined(UNITY_FORCE_LINEAR_READ_FOR_RGBM)
return pow ((unity_DynamicLightmap_HDR.x * color.a) * sqrt(color.rgb), unity_DynamicLightmap_HDR.y);
#else
return pow ((unity_DynamicLightmap_HDR.x * color.a) * color.rgb, unity_DynamicLightmap_HDR.y);
#endif
}
inline half3 DecodeDirectionalLightmap (half3 color, fixed4 dirTex, half3 normalWorld)
{
// In directional (non-specular) mode Enlighten bakes dominant light direction
// in a way, that using it for half Lambert and then dividing by a "rebalancing coefficient"
// gives a result close to plain diffuse response lightmaps, but normalmapped.
// Note that dir is not unit length on purpose. Its length is "directionality", like
// for the directional specular lightmaps.
half halfLambert = dot(normalWorld, dirTex.xyz - 0.5) + 0.5;
return color * halfLambert / max(1e-4h, dirTex.w);
}
关于.cginc这类文件到底是在哪里有的,可以去官网下载一个Shader文档,解压缩后如下所示位置。
或者直接从这里拿:链接: https://pan.baidu.com/s/1R7d5WwdiiGkJxolgS7jq-A 提取码: fmet (如失效请联系)