原文链接:Directional Lights
效果图
CustomRenderPipelineAsset.cs
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")] public class CustomRenderPipelineAsset : RenderPipelineAsset {public bool useDynamicBatching = true; //启用动态合批public bool useGPUInstancing = true; //GPU Instancingpublic bool useSRPBatcher = true; //SRP合批protected override RenderPipeline CreatePipeline () {return new CustomRenderPipeline(useDynamicBatching, useGPUInstancing, useSRPBatcher);}}
CustomRenderPipeline.cs
public class CustomRenderPipeline : RenderPipeline {CameraRenderer renderer = new CameraRenderer();bool useDynamicBatching, useGPUInstancing;public CustomRenderPipeline (bool useDynamicBatching, bool useGPUInstancing, bool useSRPBatcher) {this.useDynamicBatching = useDynamicBatching;this.useGPUInstancing = useGPUInstancing;GraphicsSettings.useScriptableRenderPipelineBatching = useSRPBatcher;GraphicsSettings.lightsUseLinearIntensity = true;}protected override void Render(ScriptableRenderContext context, Camera[] cameras) {}protected override void Render(ScriptableRenderContext context, List<Camera> cameras) {for (int i = 0; i < cameras.Count; i++) {renderer.Render(context, cameras[i], useDynamicBatching, useGPUInstancing);}}}
CameraRenderer.cs
public partial class CameraRenderer {const string bufferName = "Render Camera";static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");static ShaderTagId litShaderTagId = new ShaderTagId("CustomLit");CommandBuffer buffer = new CommandBuffer {name = bufferName};ScriptableRenderContext context;Camera camera;CullingResults cullingResults;Lighting lighting = new Lighting();public void Render(ScriptableRenderContext context, Camera camera,bool useDynamicBatching, bool useGPUInstancing) {this.context = context;this.camera = camera;#if UNITY_EDITORPrepareBuffer();PrepareForSceneWindow(); #endifif (!Cull()) {return;}Setup();lighting.Setup(context, cullingResults);DrawVisibleGeometry(useDynamicBatching, useGPUInstancing); #if UNITY_EDITORDrawUnsupportedShaders();DrawGizmos(); #endifSubmit();}bool Cull() {if (camera.TryGetCullingParameters(out ScriptableCullingParameters p)) {cullingResults = context.Cull(ref p);return true;}return false;}void Setup() {context.SetupCameraProperties(camera);CameraClearFlags flags = camera.clearFlags;buffer.ClearRenderTarget(flags <= CameraClearFlags.Depth,flags <= CameraClearFlags.Color,(flags == CameraClearFlags.Color) ? camera.backgroundColor.linear : Color.clear);buffer.BeginSample(SampleName);ExecuteBuffer();}void Submit() {buffer.EndSample(SampleName);ExecuteBuffer();context.Submit();}void ExecuteBuffer() {context.ExecuteCommandBuffer(buffer);buffer.Clear();}void DrawVisibleGeometry(bool useDynamicBatching, bool useGPUInstancing) {//先渲染不透明物体var sortingSettings = new SortingSettings(camera) {criteria = SortingCriteria.CommonOpaque};var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings) {enableDynamicBatching = useDynamicBatching,enableInstancing = useGPUInstancing};drawingSettings.SetShaderPassName(1, litShaderTagId);var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);//再渲染天空盒 context.DrawSkybox(camera);//再渲染半透明物体sortingSettings.criteria = SortingCriteria.CommonTransparent;drawingSettings.sortingSettings = sortingSettings;filteringSettings.renderQueueRange = RenderQueueRange.transparent;context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);}}
CameraRenderer.Edtor.cs
partial class CameraRenderer {#if UNITY_EDITOR//内置渲染管线的tag标识static ShaderTagId[] legacyShaderTagIds = {new ShaderTagId("Always"),new ShaderTagId("ForwardBase"),new ShaderTagId("PrepassBase"),new ShaderTagId("Vertex"),new ShaderTagId("VertexLMRGBM"),new ShaderTagId("VertexLM")};static Material errorMaterial;string SampleName { get; set; }void DrawGizmos() {if (Handles.ShouldRenderGizmos()) {context.DrawGizmos(camera, GizmoSubset.PreImageEffects);context.DrawGizmos(camera, GizmoSubset.PostImageEffects);}}//shader错误时, 显示为粉红void DrawUnsupportedShaders() {if (errorMaterial == null) {errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));}var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) {overrideMaterial = errorMaterial};for (int i = 1; i < legacyShaderTagIds.Length; i++) {drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);}var filteringSettings = FilteringSettings.defaultValue;context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);}void PrepareForSceneWindow() {if (camera.cameraType == CameraType.SceneView) {ScriptableRenderContext.EmitWorldGeometryForSceneView(camera); //渲染ugui的几何图形 }}void PrepareBuffer() {Profiler.BeginSample("Editor Only"); //Window -> Analysis -> Profile -> CPU Usage:Hierarchy中会显示buffer.name = SampleName = camera.name; //编辑器下BeginSample使用相机名字, Player下使用固定名字(Render Camera) Profiler.EndSample();}#elseconst string SampleName = bufferName; #endif}
Lighting.cs
public class Lighting {const string Sample_Name = "Lighting";const int maxDirLightCount = 4;static int dirLightCountId = Shader.PropertyToID("_DirectionalLightCount");static int dirLightColorsId = Shader.PropertyToID("_DirectionalLightColors");static int dirLightDirectionsId = Shader.PropertyToID("_DirectionalLightDirections");static Vector4[] dirLightColors = new Vector4[maxDirLightCount];static Vector4[] dirLightDirections = new Vector4[maxDirLightCount];CommandBuffer buffer = new CommandBuffer {name = "Lighting"};CullingResults cullingResults;public void Setup(ScriptableRenderContext context, CullingResults cullingResults) {this.cullingResults = cullingResults;buffer.BeginSample(Sample_Name);SetupLights();buffer.EndSample(Sample_Name);context.ExecuteCommandBuffer(buffer);buffer.Clear();}void SetupLights() {NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;int dirLightCount = 0;for (int i = 0; i < visibleLights.Length; i++) {VisibleLight visibleLight = visibleLights[i];if (visibleLight.lightType == LightType.Directional) {SetupDirectionalLight(dirLightCount++, ref visibleLight);if (dirLightCount >= maxDirLightCount) break;}}buffer.SetGlobalInt(dirLightCountId, dirLightCount);buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirections);}void SetupDirectionalLight (int index, ref VisibleLight visibleLight) {dirLightColors[index] = visibleLight.finalColor;dirLightDirections[index] = -visibleLight.localToWorldMatrix.GetColumn(2);}}
ShaderLibrary/UnityInput.hlsl
#ifndef CUSTOM_UNITY_INPUT_INCLUDED #define CUSTOM_UNITY_INPUT_INCLUDEDCBUFFER_START(UnityPerDraw)float4x4 unity_ObjectToWorld;float4x4 unity_WorldToObject;float4 unity_LODFade;real4 unity_WorldTransformParams; CBUFFER_ENDfloat4x4 unity_MatrixVP; //世界空间坐标转裁剪空间 float4x4 unity_MatrixV; //世界空间坐标转相机空间 float4x4 unity_MatrixInvV; float4x4 unity_prev_MatrixM; float4x4 unity_prev_MatrixIM; float4x4 glstate_matrix_projection; //投影矩阵 float3 _WorldSpaceCameraPos; //相机位置#endif
ShaderLibrary/Common.hlsl
#ifndef CUSTOM_COMMON_INCLUDED #define CUSTOM_COMMON_INCLUDED#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" #include "UnityInput.hlsl"//下面的宏是SpaceTransforms会用到, 不定义无法通过编译 #define UNITY_MATRIX_M unity_ObjectToWorld #define UNITY_MATRIX_I_M unity_WorldToObject #define UNITY_MATRIX_V unity_MatrixV #define UNITY_MATRIX_I_V unity_MatrixInvV #define UNITY_MATRIX_VP unity_MatrixVP #define UNITY_PREV_MATRIX_M unity_prev_MatrixM #define UNITY_PREV_MATRIX_I_M unity_prev_MatrixIM #define UNITY_MATRIX_P glstate_matrix_projection#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"float Square(float x) {return x * x; }#endif
ShaderLibrary/Surface.hlsl
#ifndef CUSTOM_SURFACE_INCLUDED #define CUSTOM_SURFACE_INCLUDED//光照射到的物体表面的相关信息 struct Surface {float3 normal; //法线float3 viewDirection; //视角方向float3 color; //光照计算前的颜色float alpha;float metallic; //金属度float smoothness; //光滑度 };#endif
ShaderLibrary/Light.hlsl
#ifndef CUSTOM_LIGHT_INCLUDED #define CUSTOM_LIGHT_INCLUDED#define MAX_DIRECTIONAL_LIGHT_COUNT 4CBUFFER_START(_CustomLight)int _DirectionalLightCount;float4 _DirectionalLightColors[MAX_DIRECTIONAL_LIGHT_COUNT];float4 _DirectionalLightDirections[MAX_DIRECTIONAL_LIGHT_COUNT]; CBUFFER_ENDstruct Light {float3 color; //光源颜色float3 direction; //光源方向 };//平行光总数量 int GetDirectionalLightCount() {return _DirectionalLightCount; }//获取某个平行光信息 Light GetDirectionalLight(int index) {Light light;light.color = _DirectionalLightColors[index].rgb;light.direction = _DirectionalLightDirections[index].xyz;return light; }#endif
ShaderLibrary/BRDF.hlsl
#ifndef CUSTOM_BRDF_INCLUDED #define CUSTOM_BRDF_INCLUDED//光照到物体表面后, 一部分被漫反射(向各个方向均匀反射), 其余部分被镜面反射(某个方向完全反射)。 注意: 折射和进入物体等情况不考虑时 //金属度决定镜面反射比例, 光滑度决定镜面反射比例中有多少比例反射出来//双向反射分布计算要用到的数据 struct BRDF {float3 diffuse; //漫反射float3 specular; //高光float roughness; //粗糙度 };#define MIN_REFLECTIVITY 0.04 //非金属的平均镜面反射比例//获取漫反射比例 float OneMinusReflectivity(float metallic) {float range = 1.0 - MIN_REFLECTIVITY;return range - metallic * range; //确保metallic=0时也有镜面反射 }//双向反射分布计算后的漫反射(会比物体原有颜色弱)和镜面反射 BRDF GetBRDF(Surface surface, bool applyAlphaToDiffuse = false) {BRDF brdf;float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);brdf.diffuse = surface.color * oneMinusReflectivity;if (applyAlphaToDiffuse) {brdf.diffuse *= surface.alpha;}brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);//迪士尼光照模型中的方法计算出粗糙度float perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);brdf.roughness = PerceptualRoughnessToRoughness(perceptualRoughness); //直观方式表示的粗糙度(即: 0~1)转换为公式使用的粗糙度return brdf; }//高光反射强度取决于视角方向和完全发射方向的正对程度, 这边使用简化版CookTorrance BRDF公式计算 float SpecularStrength(Surface surface, BRDF brdf, Light light) {// r^2 / (d^2*max(0.1, (L·H)^2)*n), r为粗糙度// d = (N·H)^2*(r^2 - 1) + 1.0001, N为表面法线方向// H = L+V, L为光源方向, V为视角方向// n = 4*r+2float3 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.0 + 2.0;return r2 / (d2 * max(0.1, lh2) * normalization); }//平行光的BRDF float3 DirectBRDF(Surface surface, BRDF brdf, Light light) {//高光反射叠加漫反射(叠加用+,混合用乘)return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse; }#endif
ShaderLibrary/Lighting.hlsl
#ifndef CUSTOM_LIGHTING_INCLUDED #define CUSTOM_LIGHTING_INCLUDED//入射光强度计算 float3 IncomingLight(Surface surface, Light light) {return saturate(dot(surface.normal, light.direction)) * light.color; }//单个入射光照到物体表面后的光照的计算 float3 GetLighting(Surface surface, BRDF brdf, Light light) {return IncomingLight(surface, light) * DirectBRDF(surface, brdf, light); }//所有光照的计算 float3 GetLighting(Surface surface, BRDF brdf) {float3 color = 0.0;for (int i = 0; i < GetDirectionalLightCount(); i++) {color += GetLighting(surface, brdf, GetDirectionalLight(i));}return color; }#endif
LitPass.hlsl
#ifndef CUSTOM_LIT_PASS_INCLUDED #define CUSTOM_LIT_PASS_INCLUDED#include "../ShaderLibrary/Common.hlsl" #include "../ShaderLibrary/Surface.hlsl" #include "../ShaderLibrary/Light.hlsl" #include "../ShaderLibrary/BRDF.hlsl" #include "../ShaderLibrary/Lighting.hlsl"TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness) UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)struct Attributes { //程序传入顶点着色器的数据float3 positionOS : POSITION; //用模型空间顶点坐标填充该变量float3 normalOS : NORMAL; //用模型空间法线方向填充该变量float2 baseUV : TEXCOORD0; //用模型的第一套纹理坐标(uv)填充该变量 UNITY_VERTEX_INPUT_INSTANCE_ID };struct Varyings { //顶点着色器传入片元着色器的数据float4 positionCS : SV_POSITION; //该变量存放了裁剪空间的顶点坐标 float3 positionWS : VAR_POSITION;float3 normalWS : VAR_NORMAL;float2 baseUV : VAR_BASE_UV;UNITY_VERTEX_INPUT_INSTANCE_ID };Varyings LitPassVertex (Attributes input) { //顶点着色器 Varyings output;UNITY_SETUP_INSTANCE_ID(input);UNITY_TRANSFER_INSTANCE_ID(input, output);output.positionWS = TransformObjectToWorld(input.positionOS); //模型空间转世界空间output.positionCS = TransformWorldToHClip(output.positionWS); //世界空间转裁剪空间output.normalWS = TransformObjectToWorldNormal(input.normalOS);float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);output.baseUV = input.baseUV * baseST.xy + baseST.zw; //应用贴图的tiling和offsetreturn output; }float4 LitPassFragment (Varyings input) : SV_TARGET { //片元着色器 UNITY_SETUP_INSTANCE_ID(input);float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV); //根据纹理坐标获取像素颜色float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);float4 base = baseMap * baseColor;#if defined(_CLIPPING)clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));#endifSurface surface;surface.normal = normalize(input.normalWS);surface.viewDirection = normalize(_WorldSpaceCameraPos - input.positionWS); //视角方向surface.color = base.rgb;surface.alpha = base.a;surface.metallic = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic);surface.smoothness =UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);#if defined(_PREMULTIPLY_ALPHA)BRDF brdf = GetBRDF(surface, true);#elseBRDF brdf = GetBRDF(surface);#endiffloat3 color = GetLighting(surface, brdf);return float4(color, surface.alpha); }#endif
Lit.shader
Shader "Custom RP/Lit" {Properties {_BaseMap("Texture", 2D) = "white" {} //主贴图_BaseColor("Color", Color) = (0.5, 0.5, 0.5, 1.0) //混合颜色_Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5[Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 0_Metallic ("Metallic", Range(0, 1)) = 0 //金属度_Smoothness ("Smoothness", Range(0, 1)) = 0.5 //光滑度 [Toggle(_PREMULTIPLY_ALPHA)] _PremulAlpha ("Premultiply Alpha", Float) = 0[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0[Enum(Off, 0, On, 1)] _ZWrite ("Z Write", Float) = 1}SubShader {Pass {Tags {"LightMode" = "CustomLit"}Blend [_SrcBlend] [_DstBlend]ZWrite [_ZWrite]HLSLPROGRAM //hlsl代码需要用这个包着#pragma target 3.5 //Shader Model版本低于3.5, 核心库无法通过编译#pragma shader_feature _CLIPPING#pragma shader_feature _PREMULTIPLY_ALPHA#pragma multi_compile_instancing#pragma vertex LitPassVertex#pragma fragment LitPassFragment#include "LitPass.hlsl"ENDHLSL}} }
翻译备忘:
We would only see this light if the camera were aligned with it
只有当相机正对它时,我们才能看到这束光
But if the surface isn't perfectly flat then the light gets scattered
如果表面不是完全光滑的, 那么光就会散射
because the fragment effectively consists of many smaller fragments that have different orientations
因为片元实际上由许多不同朝向的小片元组成
This splits the beam of light into smaller beams that go in different directions, which effectively blurs the specular reflection
这将一束光分散成更小的光(不同方向),这有效的柔化了镜面反射
Surfaces can be perfectly diffuse, perfect mirrors, or anything in between
表面可以完全漫反射, 完全镜面反射, 或介于两者之间