前幾天,想看看AMD以前的技術示範,看到了這個demo。可惜沒有AMD的顯卡,看不到的實際效果。網上資料找了半天沒有找到,昨天剛剛在案頭上發現了一個ppt,Technology Behind AMD’s “Leo Demo”。去年在GDC上發布的ppt,關於leo Demo的光照。
Leo Demo沒有選擇延遲渲染,原因有三點,一是材質太複雜,二是燈光總類都,三是支援半透明。其中用到了light culling,思想是將全局座標系內的燈光在螢幕座標系內進行分割,分成一個個小的tile,每個tile儲存自己的光照資訊。其實和戰地3裡的方法差不多,只不過戰地3用的是延遲渲染。
然後在渲染像素的時候找到對應的tile取裡面的光照資訊,完成光照計算,輸出結果。其中很重要的一點事使用了UAV(unorder access view)來儲存光照資訊,沒有這個功能是無法實現的。其實很簡單,看看就可以了。
代碼如下:
//1. preparefloat4 frustum[4];float minZ, maxZ;{ConstructFrustum( frustum );minZ = thread_REDUCE(MIN, depth );maxZ = thread_REDUCE(MAX, depth );ldsMinZ = SIMD_REDUCE(MIN, minZ );ldsMaxZ = SIMD_REDUCE(MAX, maxZ );minZ = ldsMinZ;maxZ = ldsMaxZ;}
_local u32 ldsNLights = 0;__local u32 ldsLightBuffer[MAX];//2. overlap check, accumulate in LDSfor(int i=threadIdx; i<nLights; i+=WG_SIZE){Light light = fetchAndTransform( lightBuffer[ i ] );if( overlaps( light, frustum ) && overlaps ( light, minZ, maxZ ) ){AtomicAppend( ldsLightBuffer, i );}}
//3. export to global__local u32 ldsOffset;if( threadIdx == 0 ){ldsOffset = AtomAdd( ldsNLights );globalLightStart[tileIdx] = ldsOffset;globalLightEnd[tileIdx] = ldsOffset + ldsNLights;}for(int i=threadIdx; i< ldsNLights; i+=WG_SIZE){int dstIdx = ldsOffset + i;globalLightIndexBuffer[dstIdx] = ldsLightBuffer[i];}
// BaseLighting.inc // THIS INC FILE IS ALL THE COMMON LIGHTING CODEStructuredBuffer<float4> LightParams : register(u0);StructuredBuffer<uint> LowerBoundLights : register(u1);StructuredBuffer<uint> UpperBoundLights : register(u2);StructuredBuffer<int2> LightIndexBuffer : register(u3);uint GetTileIndex(float2 screenPos){ float tileRes = (float)m_tileRes; uint numCellsX = (m_width + m_tileRes - 1)/m_tileRes; uint tileIdx = floor(screenPos.x/tileRes)+floor(screenPos.y/tileRes)*numCellsX; return tileIdx;}
StartHLSL BaseLightLoopBegin// THIS IS A MACRO, INCLUDED IN MATERIAL SHADERS uint tileIdx = GetTileIndex( pixelScreenPos ); uint startIdx = LowerBoundLights[tileIdx]; uint endIdx = UppweBoundLights[tileIdx]; [loop] for ( uint lightListIdx = startIdx; lightListIdx < endIdx; lightListIdx++ ) {int lightIdx = LightIndexBuffer[lightListIdx];// Set common light parametersfloat ndotl = max(0, dot(normal, lightVec));float3 directLight = 0;float3 indirectLight = 0; if( lightIdx >= numDirectLightsThisFrame ) { CalculateIndirectLight(lightIdx , indirectLight); } else { if( IsConeLight( lightIdx ) ) { // <<== Can add more light types here CalculateDirectSpotlight(lightIdx , directLight); } else { CalculateDirectSpherelight(lightIdx , directLight); } } float3 incomingLight = (directLight + indirectLight)*ndotl; float shadowTerm = CalcShadow();EndHLSLStartHLSL BaseLightLoopEnd }EndHLSL
#include "BaseLighting.inc"float4 PS ( PSInput i ) : SV_TARGET{ float3 totalDiffuse = 0; float3 totalSpec = GetEnvLighting();;$include BaseLightLoopBegin// unique material code goes here!! Light accumulation on the pixel for a given light// we have total incoming light and direct/indirect light components as well as material params and shadow term// use these building blocks to integrate lighting terms totalDiffuse += GetDiffuse(incomingLight); totalSpec += CalcPhong(incomingLight);$include BaseLightLoopEnd float3 finalColor = totalDiffuse + totalSpec; return float4( finalColor, 1 );}