SRP_03_DirectionalLights


一、Directional Lights

Directional Lights

物体的光照(Lighting) = Surface(材质表面属性) 与 Light 的交互

  • BRDF 解释 surface 与 light 交互过程中有多少光线被反射出去,即将 surface.color 分解成 diffuse 和 specular
  • 入射能量 = 反射光线能量 + 漫反射光线能量
  • 最终反射进入人眼睛的反射光线 = 反射光线能量 和 观察方向有关(公式见下图 【Unity 高光强度计算公式】)
  • 金属材质会完全反射光线,非金属则基本上不会反射光线,介点介质影响(大概反射0.04)
  • PBR金属工作流程,Albedo = 非金属 diffuse 颜色 + 金属 Specular 颜色

unity 高光的计算公式

image-20221117190206613

管线中可见光获取

NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;

可变长度的 For 循环在 OpenGL ES 2.0 and WebGL 1.0 graphics APIs 中不支持

#pragma target 3.5

完整代码

  • Lighting.hlsl
#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED

bool RenderingLayersOverlap (Surface surface, Light light)
{
    //OpenGL ES 2.0 及以下不支持位操作,其他支持
    return (surface.renderingLayerMask & light.renderingLayerMask) != 0;
}

float3 IncomingLight(Surface surface, Light light)
{
    return saturate(dot(surface.normal, light.direction)) * light.attenuation * light.color;
}

float3 GetLighting(Surface surface, BRDF brdf, Light light)
{
    return IncomingLight(surface, light) * DirectBRDF(surface, brdf, light);
}

float3 GetLighting(Surface surfaceWS, BRDF brdf, GI gi)
{
    ShadowData shadowData = GetShadowData(surfaceWS);
    shadowData.shadowMask = gi.shadowMask;
    float3 color = IndirectBRDF(surfaceWS, brdf, gi.diffuse, gi.specular);
    for (int i = 0; i < GetDirectionalLightCount(); i++)
    {
        Light light = GetDirectionLight(i, surfaceWS, shadowData);
        if (RenderingLayersOverlap(surfaceWS, light))
        {
            color += GetLighting(surfaceWS, brdf, light);
        }
    }
    
    return color;
}


#endif
  • BRDF.hlsl
#ifndef CUSTOM_BRDF_INCLUDED
#define CUSTOM_BRDF_INCLUDED

#define MIN_REFLECTIVITY 0.04

struct BRDF
{
    float3 diffuse;
    float3 specular;
    float roughness;
    float perceptualRoughness;
    float fresnel;
};

float OneMinusReflectivity(float metallic)
{
    float range = 1.0 - MIN_REFLECTIVITY;
    return (range - metallic * range);
}


BRDF GetBRDF(Surface surface, bool applyAlphaToDiffuse = false)
{
    BRDF brdf = (BRDF)0;
    float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);

    brdf.diffuse = surface.color * oneMinusReflectivity;

    if (applyAlphaToDiffuse)
    {
        //通过下面的方法,解决高光反射也会在 blend 中减淡的问题
        //更改blend srcAlpha oneMinusSrcAlpha 为 Blend One oneMinusSrcAlpha。并且 premultiplied alpha
        brdf.diffuse *= surface.alpha;
    }

    //因为金属基本没有diffuse,金属会影响高光颜色,不同金属会有不同的高光颜色。金属工作流程中,surface.color 金属部分绘制的是高光颜色
    //非金属的反射颜色为白色,白色 * MIN_REFLECTIVITY(非金属最小的反射率) = MIN_REFLECTIVITY
    brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);

    brdf.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);
    brdf.roughness = PerceptualRoughnessToRoughness(brdf.perceptualRoughness); //matches the Disney lighting model
    brdf.fresnel = saturate(surface.smoothness + 1.0 - oneMinusReflectivity);
    return brdf;
}

float SpecularStrength(Surface surface, BRDF brdf, Light light)
{
    float3 h = SafeNormalize(light.direction + surface.viewDirection);
    float nh2 = Square(saturate(dot(surface.normal, h)));
    float lh2 = Square(saturate(dot(light.direction, h)));
    float r2 = Square(brdf.roughness);
    float d2 = Square(nh2 * (r2 - 1.0) + 1.00001);
    float normalization = brdf.roughness * 4 + 2;
    return r2 / (d2 * max(0.1, lh2) * normalization);
}

float3 DirectBRDF(Surface surface, BRDF brdf, Light light)
{
    return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
}

float3 IndirectBRDF(Surface surface, BRDF brdf, float3 diffuse, float3 specular)
{
    float fresnelStrength = surface.fresnelStrength * Pow4(1.0 - saturate(dot(surface.normal, surface.viewDirection)));
    float3 reflection = specular * lerp(brdf.specular, brdf.fresnel, fresnelStrength);
    reflection /= brdf.roughness * brdf.roughness + 1.0;
    return (diffuse * brdf.diffuse + reflection) * surface.occlusion;
}

#endif

二、Custom Shader GUI

结构:

using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

public class CustomShaderGUI : ShaderGUI 
{
	private MaterialEditor _editor;
    private Object[] _materials;
    private MaterialProperty[] _properties;
    
	public override void OnGUI (MaterialEditor materialEditor, MaterialProperty[] properties) 
    {
		base.OnGUI(materialEditor, properties);
        _editor = materialEditor;
        _materials = materialEditor.targets;
        _properties = properties;
	}
}

判断某个属性是否存在

private bool HasProperty(string name)
{
    return FindProperty(name, _properties, false) != null;
}

设置属性

private bool SetProperty(string name, float value)
{
    MaterialProperty property = FindProperty(name, _properties, false);
    if (property != null)
    {
        property.floatValue = value;
        return true;
    }

    return false;
}

显示多个不同值的情况

EditorGUI.showMixedValue = property.hasMultipleDifferentValues;

显示自发光属性

_editor.LightmapEmissionProperty();

Shader 中启用

Shader "CustomRP/Lit"
{
    Properties{...}
    SubShader{...}
    
    CustomEditor "CustomRP.Editor.CustomShaderGUI"
}

文章作者: 血魂S
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 血魂S !
  目录