架構流程如下:
Light List相關(Setup light constants pass):
從代碼得知,Culling list裡的Light list是目前camera view所看到的lights,經過最大燈光的
數量的限制及SetGlobalVetorArray會造成物件燈光計算的錯誤,因為是view culling list,並不是Object List…
void SetupAdditionalLightConstants(CommandBuffer cmd, ref LightData lightData){
List
if (lightData.totalAdditionalLightsCount > 0)
{
int localLightsCount = 0;
for (int i = 0; i < lights.Count && localLightsCount < maxVisibleLocalLights; ++i)
{
VisibleLight light = lights[i];
if (light.lightType != LightType.Directional)
{
InitializeLightConstants(lights, i, out m_LightPositions[localLightsCount],
out m_LightColors[localLightsCount],
out m_LightDistanceAttenuations[localLightsCount],
out m_LightSpotDirections[localLightsCount],
out m_LightSpotAttenuations[localLightsCount]);
localLightsCount++;
}
}
cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightCount, new Vector4(lightData.pixelAdditionalLightsCount,
lightData.totalAdditionalLightsCount, 0.0f, 0.0f));
// if not using a compute buffer, engine will set indices in 2 vec4 constants
// unity_4LightIndices0 and unity_4LightIndices1
if (perObjectLightIndices != null)
cmd.SetGlobalBuffer("_LightIndexBuffer", perObjectLightIndices);
}
else
{
cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightCount, Vector4.zero);
}
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightPosition, m_LightPositions);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightColor, m_LightColors);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightDistanceAttenuation, m_LightDistanceAttenuations);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightSpotDir, m_LightSpotDirections);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightSpotAttenuation, m_LightSpotAttenuations);
}
Object light list的處理:原來Unity底層有支援RendererConfiguration.PerObjectLightIndices8,
可以用來解決這個問題,如果有支援StructuredBuffer的話,可以走另一個流程
RendererConfiguration.ProvideLightIndices,相關代碼如下:
//這裡主要是在處理方向光需要從project light indices中移掉,交由MainLight接手。
public void SetupPerObjectLightIndices(ref CullResults cullResults, ref LightData lightData)
{
...
}
public static RendererConfiguration GetRendererConfiguration(int localLightsCount){
RendererConfiguration configuration = RendererConfiguration.PerObjectReflectionProbes | RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbe;
if (localLightsCount > 0)
{
if (useComputeBufferForPerObjectLightIndices)
configuration |= RendererConfiguration.ProvideLightIndices;
else
configuration |= RendererConfiguration.PerObjectLightIndices8;
}
return configuration;
}
相關Shader如下:會利用GetLight這個函式來取得正確的Light index。
Light GetLight(half i, float3 positionWS)
{
LightInput lightInput;
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
int lightIndex = _LightIndexBuffer[unity_LightIndicesOffsetAndCount.x + i];
#else
// The following code is more optimal than indexing unity_4LightIndices0.
// Conditional moves are branch free even on mali-400
half i_rem = (i < 2.0h) ? i : i - 2.0h;
half2 lightIndex2 = (i < 2.0h) ? unity_4LightIndices0.xy : unity_4LightIndices0.zw;
int lightIndex = (i_rem < 1.0h) ? lightIndex2.x : lightIndex2.y;
#endif
// The following code will turn into a branching madhouse on platforms that don't support
// dynamic indexing. Ideally we need to configure light data at a cluster of
// objects granularity level. We will only be able to do that when scriptable culling kicks in.
// TODO: Use StructuredBuffer on PC/Console and profile access speed on mobile that support it.
lightInput.position = _AdditionalLightPosition[lightIndex];
lightInput.color = _AdditionalLightColor[lightIndex].rgb;
lightInput.distanceAttenuation = _AdditionalLightDistanceAttenuation[lightIndex];
lightInput.spotDirection = _AdditionalLightSpotDir[lightIndex];
lightInput.spotAttenuation = _AdditionalLightSpotAttenuation[lightIndex];
half4 directionAndRealtimeAttenuation = GetLightDirectionAndAttenuation(lightInput, positionWS);
Light light;
light.index = lightIndex;
light.direction = directionAndRealtimeAttenuation.xyz;
light.attenuation = directionAndRealtimeAttenuation.w;
light.subtractiveModeAttenuation = lightInput.distanceAttenuation.w;
light.color = lightInput.color;
return light;
}
切割Pass原因:
主要是為容易切割處理流程、共用程式碼、以及提供足夠的時機做相關後置特效處理。另外也利用了切pass的時機,copy了render過程的結果,以便於除錯及觀察繪製流程。
接下來要做的事:
- 影子pass處理,預計會採用跟Unity一樣的CSM shadow map全場景投影的做法
- PBR材質
- 支持forward plus rendering,目前Unity是將這部份放在HRDP,估計應該是因為
compute shader light list的問題,如果最後不行的話,最差的狀況至少也可以先為
Vulkan做準備。
除錯資料(左上開始的小圖),由左而右,是繪製流程的順序,依序為
after draw opaques, depth, after draw sky, after draw transparencies.
另外也跟LWSRP一樣支持render scale,簡單來說就是不直接Render到viewport上,而是先
畫到RenderTexture,最後再Final Blt到viewport上,好處是:
- 讓3D採用較低的解析度,可以大大減少GPU處理像素的數量,提升效能。
- 可以直接使用NativeDepthBuffer,不用增加DrawCall。
- 由GUI一般會使用point filter,而3D大都會採用linear filter,可以切開。
相關影片如下:
Dream continues in...
沒有留言:
張貼留言