Unity3D Rendering复习之瞎几把写(三),单光源光照

原文:https://catlikecoding.com/unity/tutorials/rendering/part-4/

译文:http://gad.qq./com/program/translateview/7173932


第四章:基本光照知识点总结

知识点:

·        将法线从物体空间转换到世界空间。

·        使用方向光。

·        计算漫反射和镜面高光反射。

·        实现能量守恒。

·        使用金属的工作流程。

·        利用Unity的基于物理规则渲染的算法。

 

1法线从物体空间转换到世界空间

动态批次合并(DyDynamic Batching

 Unity动态地将小的网格合并在一起,以减少绘制调用。球体的网格对动态批次合并而言太大了,因此球体的网格不受影响。但是动态批次合并会对立方体有影响。

世界坐标空间中的法线

InterpolatorsMyVertexProgram (VertexData v) {

    Interpolatorsi;

    i.position= mul(UNITY_MATRIX_MVP, v.position);

    i.normal = mul(unity_ObjectToWorld, float4(v.normal, 0));

    i.uv= TRANSFORM_TEX(v.uv, _MainTex);

    returni;}

 v.normal是一个float3类型,unity_ObjectToWorld 4*4矩阵,将点(或者向量)从模型坐标系变换到世界坐标系

法向量归一化:

i.normal =normalize(i.normal);

法向量的拉伸会发生角度的变动

X轴进行缩放,顶点和法线都变为½

X轴进行缩放,顶点变为½,而法线加倍。

正确的做法

UnityCG包含一个方便的UnityObjectToWorldNormal

InterpolatorsMyVertexProgram (VertexData v) {

    Interpolators i;

    i.position = mul(UNITY_MATRIX_MVP,v.position);

    i.normal =UnityObjectToWorldNormal(v.normal);

    i.uv = TRANSFORM_TEX(v.uv, _MainTex);

    returni;

}

 

 

重新归一化

在片段着色器中,法线或通过内插值器,它会变得比单位长度更短一些,需要进行重新归一化,(也可以选择不归一化,因为误差通常很小,这是移动设备中常见的优化)

 

2漫反射的渲染Diffuse

兰伯特余弦定理:漫反射光的量与光的入射方向和表面法线之间的角度的余弦成正比

点积: 个向量之间的点积在几何上定义为A·B = ||A || || B || cosθ

在两个单位向量的情况下,A·B =cosθ

UnityStandardBRDF.cginc导入文件定义了方便的DotClaped函数,确保点积结果在0-1之间

 

光源

平行光被认为是无限远的

UnityShaderVariables里面定义了float4_WorldSpaceLightPos0,光源的位置有四个分量,因为这些是齐次坐标。因此,我们的方向光的第四个分量是0,(齐次坐标系中,w=1表示点)

渲染路径有:前向渲染路径,延迟渲染路径

使用前向渲染路径,在shader中必须有个一通道使用LightMode =“ForwardBase”

 

光源可以有颜色,通过fixed4 _LightColor0

 

反射率Albedo

材质的漫反射率的颜色被称为材质的反射率。因此,它描述了有多少红色、绿色和蓝色被漫反射。其余的部分被吸收。

  float3diffuse = albedo * lightColor * DotClamped(lightDir, i.normal);

漫反射颜色 = 反射率 * 光颜色* 漫反射光强度

 

3镜面高光着色(Specular Shading

视点方向

float3 viewDir = normalize(_WorldSpaceCameraPos -i.worldPos);

反射光方向

float3 reflectionDir= reflect(-lightDir, i.normal);

Blinn反射模型

视点方向与反射方向的点积,光滑度控制分量大小

镜面高光 =pow(DotClamped(viewDir, reflectionDir),_Smoothness * 100)

 

Blinn-Phong

使用光的入射方向和视线方向之间的半矢量halfVector,法线normal和半矢量之间的点积可以确定镜面高光的贡献。

halfVector = normalize(lightDir + viewDir)

镜面高光 =pow(DotClamped(halfVector, i.normal),_Smoothness * 100)

优点:可以产生更大个高亮,更接近现实

(镜面高光可以带颜色)

 

能量守恒

简单把结果 = 漫反射+镜面高光反射,物体太亮了,可以简单将漫反射乘以1减去镜面高光,来调整albedo *= 1 - _SpecularTint.rgb彩色能量守恒

Unity函数EnergyConservationBetweenDiffuseAndSpecular(half3 albedo, half3 specColor, out half oneMinusReflectivity)

 

4.Metallic(这个好些)

金属度(金属没有反射率),通过调节金属度来调节漫反射与镜面反射的比例。

specular *=  albedo * _Metallic

albedo * = 1-_Metallic

Unity中也自带函数

 

5. Physically-Based Shading

目前行业热点,代替了前面的Blinn-Phong模型,

Unity中在"UnityPBSLighting.cginc"文件中

BRDF双向反射分布数(不懂都是公式)

要使用它,在shadershader level要高于3.0

#pragma target 3.0

float4 MyFragmentProgram (Interpolators i) : SV_TARGET {
        i.normal = normalize(i.normal);
        float3 lightDir = _WorldSpaceLightPos0.xyz;
        float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
        float3 lightColor = _LightColor0.rgb;
        float3 albedo = tex2D(_MainTex, i.uv).rgb * _Tint.rgb;
 
        float3 specularTint;
        float oneMinusReflectivity;
        albedo = DiffuseAndSpecularFromMetallic(
                albedo, _Metallic, specularTint, oneMinusReflectivity
               );
        
        UnityLight light;
        light.color = lightColor;
        light.dir = lightDir;
        light.ndotl = DotClamped(i.normal, lightDir);
        UnityIndirect indirectLight;
        indirectLight.diffuse = 0;
        indirectLight.specular = 0;
 
        return UNITY_BRDF_PBS(
               albedo, specularTint,
               oneMinusReflectivity, _Smoothness,
        i.normal, viewDir,
        light, indirectLight
        );
}

UNITY_BRDF_PBS8个参数,漫反射颜色,高光颜色,反射率,光滑度,法向,视点方向,直接光源和间接光

猜你喜欢

转载自blog.csdn.net/lhcmt1/article/details/80667643