如图所示:
这个地面是绿色的,在没有烘焙之前,上面的球体和立方体都是黑色的。
地面使用的材质是A,它使用的shader是unity自己的standard着色器。
上面的球体和立方体使用的材质B,它使用的shader是我们自己编写的shader,这个在博客:
https://blog.csdn.net/wodownload2/article/details/90450421
中有具体的内容。就是采样烘焙之后的光照贴图。
烘焙之后:
球体和上面的立方体都会被地板的绿色所影响。
也就是说对于自发光的物体,经过烘焙之后,它会对周围的物体产生影响,其间接光信息会被记录在光照贴图中。
金属工作流:DiffuseAndSpecularFromMetallic
关于这个话题,参考:https://catlikecoding.com/unity/tutorials/rendering/part-4/
4.3节 金属工作流
我们日常打交道的有两个基本的分类,一个是金属,另外一个非金属。非金属也叫绝缘材质。目前,我们可以通过设置很强的specular tint来创建一个金属。而用一个很弱的单色镜面反射,来创建一个绝缘材质,这个就是镜面反射工作流。
如果我们能在金属和非金属之间切换这样会更简单一些。由于金属没有漫反射,所以我们能够使用颜色数据来代替specular tint。而非金属呢没有镜面反射,所以我们根本不需要specular tint。这个就是金属工作流。
哪个是更好的工作流呢?
两个方法都很好。这就是为啥unity对每个方法都实现了一个标准的shader。金属工作流更加的简单,因为你只有一个滑动条和一个颜色。这个能够很好的创建写实的材质了。镜面工作流也可以产生同样的效果,但是你需要更多的控制,非写实的材质也能被创建出来。
我们可以使用另外一个滑动条,作为金属开关,来代替specular tint。典型的,它应该是0或者1,因为一个东西要么是金属,要么是非金属。在0到1之间,则是金属和非金属的混合。
Properties {
_Tint ("Tint", Color) = (1, 1, 1, 1)
_MainTex ("Albedo", 2D) = "white" {}
// _SpecularTint ("Specular", Color) = (0.5, 0.5, 0.5)
_Metallic ("Metallic", Range(0, 1)) = 0
_Smoothness ("Smoothness", Range(0, 1)) = 0.1
}
…
// float4 _SpecularTint;
float _Metallic;
float _Smoothness;
这样我们就可以从albedo和metallic属性中得到specular tint了。albedo就简单通过1-metallic 得到。
float3 specularTint = albedo * _Metallic;
float oneMinusReflectivity = 1 - _Metallic;
// albedo = EnergyConservationBetweenDiffuseAndSpecular(
// albedo, _SpecularTint.rgb, oneMinusReflectivity
// );
albedo *= oneMinusReflectivity;
float3 diffuse =
albedo * lightColor * DotClamped(lightDir, i.normal);
float3 halfVector = normalize(lightDir + viewDir);
float3 specular = specularTint * lightColor * pow(
DotClamped(halfVector, i.normal),
_Smoothness * 100
);
但是,这个过于简化。因为即使是纯的绝缘体也会有一点的镜面反射。所以镜面反射强度和反射值和金属滑动条的值不匹配。这个和颜色空间有关系,幸运的是,UnityStandardUtils有一个方法DiffuseAndSpecularFromMetallic ,为我们考虑到这个问题了。
float3 specularTint; // = albedo * _Metallic;
float oneMinusReflectivity; // = 1 - _Metallic;
// albedo *= oneMinusReflectivity;
albedo = DiffuseAndSpecularFromMetallic(
albedo, _Metallic, specularTint, oneMinusReflectivity
);
一个细节就是金属滑动条支持gamma空间。但是简单的值是不会被unity进行gamma矫正的,当使用线性空间的时候。我们可以使用Gamma属性来告诉unity,我们需要对metallic滑动条进行gamma矫正。
[Gamma] _Metallic ("Metallic", Range(0, 1)) = 0
不幸的是,镜面反射对于非金属材质表现还是比较模糊的,所以我们需要一个更好的计算光照的方法,这个就是基于物理的渲染PBS。
如果上面的屋顶是透明的情况,烘焙有没有影响呢。
透明物体怎么写shader
下面我么要写一个透明的物体的shader。
透明物体首先要有要给标签,而且这个标签是subshader层级的, 不能放在pass中。
我们提供一个枚举来来选择物体是否是透明物体。
public enum ObjectType
{
Opaque,
Transparent,
}
下面是shader代码:
Shader "My/My First Lighing Shader"
{
Properties
{
_Color("Tint",Color)= (1,1,1,1)
_MainTex("MainTex",2D) = "white"{}
[HideInInspector] _SrcBlend("_SrcBlend", Float) = 1
[HideInInspector] _DstBlend("_DstBlend", Float) = 0
[HideInInspector] _zWrite("_ZWrite", Float) = 1
}
SubShader
{
Pass
{
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
CGPROGRAM
#pragma vertex MyVertexProgram
#pragma fragment MyFragmentProgram
#pragma multi_compile _ _Transparent
#include "UnityCG.cginc"
#include "My Lighting.cginc"
ENDCG
}
}
CustomEditor "ShaderUI"
}
需要一个变体_Tranparent,所以使用#pragma multi_compile _ _Transparent
然后需要在不透明的时候,设置一个渲染模式。在透明的时候,需要另外要给渲染模式。
public struct RenederSettings
{
public RenderQueue queue;
public BlendMode srcBlend, dstBlend;
public bool zWrite;
public static RenederSettings[] settings =
{
new RenederSettings()
{
queue = RenderQueue.Geometry, //不透明物体
srcBlend = BlendMode.One, //混合方式 1 0
dstBlend = BlendMode.Zero,
zWrite = true //不透明物体是开启深度测试,并且写入
},
new RenederSettings()
{
queue = RenderQueue.Transparent, //透明物体
srcBlend = BlendMode.SrcAlpha, //源alpha
dstBlend = BlendMode.OneMinusSrcAlpha, //1-srcAlpha
zWrite = false //关闭深度写入,防止发生闪烁
}
};
}
#if !defined(MY_LIGHTINGdddd)
#define MY_LIGHTINGdddd
#include "Lighting.cginc"
float4 _Color;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
struct VertexData
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
float2 uv1:TEXCOORD1;
};
struct Interpolators
{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float2 lightmapUV:TEXCOORD1;
};
struct FragmentOutput
{
float4 color:SV_Target;
};
Interpolators MyVertexProgram(VertexData v)
{
Interpolators i;
i.pos = UnityObjectToClipPos(v.vertex);
i.uv = TRANSFORM_TEX(v.uv, _MainTex);
i.lightmapUV = v.uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
return i;
}
FragmentOutput MyFragmentProgram(Interpolators i)
{
FragmentOutput output;
#if defined(_Transparent)
output.color = tex2D(_MainTex, i.uv)*_Color.a;
#else
output.color = float4(DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUV)),1);
#endif
return output;
}
#endif
如果是透明物体,则采样主纹理和乘以_Color.a;
如果是不透明物体,则直接采样光照贴图。