Saturday, May 23, 2015

Wolfgang Engel, Light Pre-Pass рэндэрлэгч

эх сурвалж : http://diaryofagraphicsprogrammer.blogspot.com/2008/03/light-pre-pass-renderer.html Sunday, March 16, 2008



Өнгөрсөн 6 сард шинэ рэндэрлэгчийн загвар гаргах санаа төрсөн бөгөөд үүнийгээ ligh pre-pass рэндэрлэгч нэрлэв. Санаа нь гэвэл Z buffer ээ эхлээд дараагаар нь normal уудаа render target дотор дүүргэж хадгалах байв. normal болон Z утгууд агуулсан G buffer л гэсэн үг ... гэхдээ deferred рэндэрлэгчтэй харьцуулвал ямар ч diffuse, specular, material index, position мэтийн өгөгдлүүдийг энэ stage дээр хадгалдаггүй байв.
Дараа нь light buffer ийг гэрлийн шинж чанаруудыг нь тусган дүүргэнэ. Тэхээр энэ санаа бол гэрэл болон material ийн шинж чанаруудыг нь ялгаж авч үздэг бн. Нэг point light ийн хувьд гэрлийн тэгшитгэлийг нь хармаар байвал иймэрхүү зүйл гарч ирнэ.

Color = Ambient + Shadow * Att * (N.L * DiffColor * DiffIntensity * LightColor + R.V^n * SpecColor * SpecIntensity * LightColor)


Гэрлийн утгууд нь :
- N.L
- LightColor
- R.V^n
- Attenuation

Тэхээр чиний хийх боломжтой зүйл бол гэрлийн тэгшитгэлийг render target дотор байгаа гэрэл бүрт зориулан ажиллуулж рэндэр хийхийн оронд 8:8:8:8 render target дотор зөвхөн гэрлийн утгуудыг рэндэрлэх явдал юм. Рэндэр хийх боломжтой 4-н суваг бий :

LightColor.r * N.L * Att
LightColor.g * N.L * Att
LightColor.b * N.L * Att
R.V^n * N.L * Att

Энэ юу гэсэн үг вэ гэхээр энэ тохиргоонд ямар нэгэн specular өнгө байхгүй ... үүнийгээ дараагаар нь өргөтгөл маягаар хэрэглэх боломжтой.
light buffer дотор хадгалж байгаа эх кодыг харуулбал:

half4 ps_main( PS_INPUT Input ) : COLOR
{
half4 G_Buffer = tex2D( G_Buffer, Input.texCoord );

// Compute pixel position
half Depth = UnpackFloat16( G_Buffer.zw );
float3 PixelPos = normalize(Input.EyeScreenRay.xyz) * Depth;

// Compute normal
half3 Normal;
Normal.xy = G_Buffer.xy*2-1;
Normal.z = -sqrt(1-dot(Normal.xy,Normal.xy));

// Computes light attenuation and direction
float3 LightDir = (Input.LightPos - PixelPos)*InvSqrLightRange;
half Attenuation = saturate(1-dot(LightDir / LightAttenuation_0, LightDir / LightAttenuation_0));
LightDir = normalize(LightDir);

// R.V == Phong
float specular = pow(saturate(dot(reflect(normalize(-float3(0.0, 1.0, 0.0)), Normal), LightDir)), SpecularPower_0);

float NL = dot(LightDir, Normal)*Attenuation; 

return float4(DiffuseLightColor_0.x*NL, DiffuseLightColor_0.y*NL, DiffuseLightColor_0.z*NL, specular * NL); 
}

Бүх гэрлүүд light buffer дотор alpha-blended хийгдсэний дараагаар forward rendering лүү шилжээд гэрлийн тэгшитгэлийг дахин байгуулж ажиллуулна. Кодыг хамгийн хялбараар нь харвал

float4 ps_main( PS_INPUT Input ) : COLOR0
{
float4 Light = tex2D( Light_Buffer, Input.texCoord );
float3 NLATTColor = float3(Light.x, Light.y, Light.z);
float3 Lighting = NLATTColor + Light.www;

return float4(Lighting, 1.0f);
}

Энэ арга бол Damian Trebilco ийн бодож олсон Light Indexed Renderer хэмээх аргын хувьд шууд өрсөлдөгч нь болох юм.
Энэ аргыг deferred renderer аргатай харьцуулсан жишээ надад байна гэхдээ Damian-ийхтай харьцуулсангүй. Миний итгэж байгаагаар Damian ий арга нь material system ийн хувьд минийхээс илүү уян хатан байж болох юм гэхдээ Light Pre-Pass рэндэрлэгчид indexing ажиллуулах шаардлага байхгүй. ATI RADEON 8500 мэтийн хуучин hardware дээр хүртэл ажиллана яагаад вэ гэхлээр зөвхөн Z pre-pass ийг ажиллуулаад normal-уудыг л урьдчилан хадгалах шаардлагатай.
Энэ зурагт дөрвөн ширхэг point-light ийг Light Pre-Pass алгоритмаар ажиллуулан харуулжээ. Гэрлийн үүсгүүрүүдийн хувьд ямар нэгэн хязгаарлалт байхгүй
Энэ зурагт дээрхитэй ижилхэн скэнэг deferred rendering алгоритм ашигласан байдлаар харуулжээ. Light Pre-Pass renderer тэй ажиллуулсанаас ямар нэгэн байдлаар өөр үр дүнд хүрэх ёсгүй юм.

Дизайны хувьд үнэхээр уян хатан бөгөөд өргөтгөх бололцоотой. Энэ хандлагын хувьд асуудал байж болох зүйл нь гэвэл material system байж болох юм. Ялгаатай утгуудыг light buffer тодор хадгалах боломжтой эсвэл дээрхи утгуудыг хэрэглээд сонирхолтой материулууд үүсгэж болно. Жишээлбэл per-object specular highlight ийг alpha channel доторхи утгуудыг аваад үүнлүүгээ power function хэрэглэх аль эсвэл power утгуудыг өөр суваг дотор хадгалах замаар хийх боломжтой.
Энэ дурдсан хандлагаар нийт байж болох боломжуудын багахан хэсгийг нь л дурдлаа.
material system хэрэгжүүлэхэд дараах хоёр зүйлсийг хийх боломжтой :
deferred render-т хэрэглэгддэгчилэн material-ийн id-г normal map-тай хамт аль эсвэл alpha channel дотор хадгалж болно. Diffuse болон specular утгуудыг forward rendering pass-аас авч дахин үүсгэж болно. Тусдаа channel дотор хадгалах хэрэгтэй ганц зүйл гэвэл N.L * Att юм. Ийм аргыг хэрэглэн R.V^n ийг specular channel дэх утгыг N.L*Att утгаар хуваан гаргаж авах боломжтой.

(R.V^n * N.L * Att) / (N.L * Att)

Эд нар бол бүх гэрлийн эх үүсгүүрүүдийг төлөөлж чадах утгууд юм.