最近在卷UE5的自定义光照模型,所以就写一个笔记,记录一下/Recently, I’ve been experimenting with custom shading models in Unreal Engine 5 (UE5), so I decided to write some notes to keep track of my progress.
分三个模块,1.拉取源代码,2.C++部分添加custom shading model,3.shader 部分/Divide it into three modules: 1. Fetching the source code, 2. Adding the custom shading model in C++, 3. Working on the shader part.
一. 拉取UE源代码/Fetching the source code
主要步骤还是根据UE官方档案来操作 从源代码构建虚幻引擎 | 虚幻引擎文档 (unrealengine.com)/The main steps to build Unreal Engine from source code should be followed based on the official documentation provided by Unreal Engine.
二.C++部分添加Custom Shading Model/To add a custom shading model in C++
To make Unreal Engine aware of your custom shading model and have it appear in the dropdown menu of the lighting model, you need to modify the EngineTypes.h and MaterialShader.cpp files. The essence of this modification is to add a new enumeration.
At this point, we have completed the modifications in the C++ part. Overall, it wasn’t difficult; it was mostly a matter of following a similar pattern. Let’s compile the engine now to see if our changes are successful.
接着我们开始修改shader部分
Let’s continue with the modification of the shader part.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;
[System.Serializable]
public struct RenderFeatureToggle
{
public ScriptableRendererFeature feature;
public bool isEnabled;
}
[ExecuteAlways]
public class RenderFeatureToggler : MonoBehaviour
{
[SerializeField]
private List<RenderFeatureToggle> renderFeatures = new List<RenderFeatureToggle>();
[SerializeField]
private UniversalRenderPipelineAsset pipelineAsset;
private void Update()
{
foreach (RenderFeatureToggle toggleObj in renderFeatures)
{
toggleObj.feature.SetActive(toggleObj.isEnabled);
}
}
}
改一下可以用脚本控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;
[System.Serializable]
public struct RenderFeatureToggle
{
public ScriptableRendererFeature feature;
public bool isEnabled;
}
[ExecuteAlways]
public class RenderFeatureToggler : MonoBehaviour
{
[SerializeField]
private List<RenderFeatureToggle> renderFeatures = new List<RenderFeatureToggle>();
[SerializeField]
private UniversalRenderPipelineAsset pipelineAsset;
private void Update()
{
//foreach (RenderFeatureToggle toggleObj in renderFeatures)
//{
// toggleObj.feature.SetActive(toggleObj.isEnabled);
//}
}
public void OpenRenderFeatureToggle()
{
foreach (RenderFeatureToggle toggleObj in renderFeatures)
{
toggleObj.feature.SetActive(true);
}
}
public void OffRenderFeatureToggle()
{
foreach (RenderFeatureToggle toggleObj in renderFeatures)
{
toggleObj.feature.SetActive(false);
}
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
public class FadingObject : MonoBehaviour, IEquatable<FadingObject>
{
public List<Renderer> Renderers = new List<Renderer>();
public Vector3 Position;
public List<Material> Materials = new List<Material>();
[HideInInspector]
public float InitialAlpha;
//public float InitialFadeScale;
private void Awake()
{
Position = transform.position;
if (Renderers.Count == 0)
{
Renderers.AddRange(GetComponentsInChildren<Renderer>());
}
foreach(Renderer renderer in Renderers)
{
Materials.AddRange(renderer.materials);
}
InitialAlpha = Materials[0].color.a;
Materials[0].SetFloat("_DitherThreshold", 1f);
}
public bool Equals(FadingObject other)
{
return Position.Equals(other.Position);
}
public override int GetHashCode()
{
return Position.GetHashCode();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FadeObjectBlockingObject : MonoBehaviour
{
[SerializeField]
private LayerMask LayerMask;
[SerializeField]
private Transform Target;
[SerializeField]
private Camera Camera;
[SerializeField]
[Range(0, 1f)]
private float FadedAlpha = 0.33f;
[SerializeField]
private bool RetainShadows = true;
[SerializeField]
private Vector3 TargetPositionOffset = Vector3.up;
[SerializeField]
private float FadeSpeed = 1;
[Header("Read Only Data")]
[SerializeField]
private List<FadingObject> ObjectsBlockingView = new List<FadingObject>();
private Dictionary<FadingObject, Coroutine> RunningCoroutines = new Dictionary<FadingObject, Coroutine>();
private RaycastHit[] Hits = new RaycastHit[10];
private void OnEnable()
{
StartCoroutine(CheckForObjects());
}
private IEnumerator CheckForObjects()
{
while (true)
{
int hits = Physics.RaycastNonAlloc(
Camera.transform.position,
(Target.transform.position + TargetPositionOffset - Camera.transform.position).normalized,
Hits,
Vector3.Distance(Camera.transform.position, Target.transform.position + TargetPositionOffset),
LayerMask
);
if (hits > 0)
{
for (int i = 0; i < hits; i++)
{
FadingObject fadingObject = GetFadingObjectFromHit(Hits[i]);
if (fadingObject != null && !ObjectsBlockingView.Contains(fadingObject))
{
if (RunningCoroutines.ContainsKey(fadingObject))
{
if (RunningCoroutines[fadingObject] != null)
{
StopCoroutine(RunningCoroutines[fadingObject]);
}
RunningCoroutines.Remove(fadingObject);
}
RunningCoroutines.Add(fadingObject, StartCoroutine(FadeObjectOut(fadingObject)));
ObjectsBlockingView.Add(fadingObject);
}
}
}
FadeObjectsNoLongerBeingHit();
ClearHits();
yield return null;
}
}
private void FadeObjectsNoLongerBeingHit()
{
List<FadingObject> objectsToRemove = new List<FadingObject>(ObjectsBlockingView.Count);
foreach (FadingObject fadingObject in ObjectsBlockingView)
{
bool objectIsBeingHit = false;
for (int i = 0; i < Hits.Length; i++)
{
FadingObject hitFadingObject = GetFadingObjectFromHit(Hits[i]);
if (hitFadingObject != null && fadingObject == hitFadingObject)
{
objectIsBeingHit = true;
break;
}
}
if (!objectIsBeingHit)
{
if (RunningCoroutines.ContainsKey(fadingObject))
{
if (RunningCoroutines[fadingObject] != null)
{
StopCoroutine(RunningCoroutines[fadingObject]);
}
RunningCoroutines.Remove(fadingObject);
}
RunningCoroutines.Add(fadingObject, StartCoroutine(FadeObjectIn(fadingObject)));
objectsToRemove.Add(fadingObject);
}
}
foreach(FadingObject removeObject in objectsToRemove)
{
ObjectsBlockingView.Remove(removeObject);
}
}
private IEnumerator FadeObjectOut(FadingObject FadingObject)
{
foreach (Material material in FadingObject.Materials)
{
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.SetInt("_Surface", 1);
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
material.SetShaderPassEnabled("DepthOnly", false);
material.SetShaderPassEnabled("SHADOWCASTER", RetainShadows);
material.SetOverrideTag("RenderType", "Transparent");
material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
}
float time = 0;
while (FadingObject.Materials[0].color.a > FadedAlpha)
{
foreach (Material material in FadingObject.Materials)
{
if (material.HasProperty("_BaseColor"))
{
float a = Mathf.Lerp(FadingObject.InitialAlpha, FadedAlpha, time * FadeSpeed);
material.color = new Color(
material.color.r,
material.color.g,
material.color.b,
a
);
//print("_1");
//Setting fade value
material.SetFloat("_DitherThreshold", a);
}
}
time += Time.deltaTime;
yield return null;
}
if (RunningCoroutines.ContainsKey(FadingObject))
{
StopCoroutine(RunningCoroutines[FadingObject]);
RunningCoroutines.Remove(FadingObject);
}
}
private IEnumerator FadeObjectIn(FadingObject FadingObject)
{
float time = 0;
while (FadingObject.Materials[0].color.a < FadingObject.InitialAlpha)
{
foreach (Material material in FadingObject.Materials)
{
if (material.HasProperty("_BaseColor"))
{
float a = Mathf.Lerp(FadedAlpha, FadingObject.InitialAlpha, time * FadeSpeed);
material.color = new Color(
material.color.r,
material.color.g,
material.color.b,
a
);
//Setting fade value
material.SetFloat("_DitherThreshold", a);
}
}
time += Time.deltaTime;
yield return null;
}
foreach (Material material in FadingObject.Materials)
{
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_ZWrite", 1);
material.SetInt("_Surface", 0);
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry;
material.SetShaderPassEnabled("DepthOnly", true);
material.SetShaderPassEnabled("SHADOWCASTER", true);
material.SetOverrideTag("RenderType", "Opaque");
material.DisableKeyword("_SURFACE_TYPE_TRANSPARENT");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
}
if (RunningCoroutines.ContainsKey(FadingObject))
{
StopCoroutine(RunningCoroutines[FadingObject]);
RunningCoroutines.Remove(FadingObject);
}
}
private void ClearHits()
{
System.Array.Clear(Hits, 0, Hits.Length);
}
private FadingObject GetFadingObjectFromHit(RaycastHit Hit)
{
return Hit.collider != null ? Hit.collider.GetComponent<FadingObject>() : null;
}
}