WebGL లో క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ గురించి తెలుసుకోండి, ఇది నిజ సమయంలో వందలాది డైనమిక్ లైట్లను రెండర్ చేయడానికి ఒక శక్తివంతమైన టెక్నిక్. దీనిలోని ముఖ్యమైన భావనలు మరియు ఆప్టిమైజేషన్ వ్యూహాలను నేర్చుకోండి.
పనితీరును అన్లాక్ చేయడం: WebGL క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ మరియు లైట్ ఇండెక్సింగ్ ఆప్టిమైజేషన్ పై ఒక లోతైన విశ్లేషణ
వెబ్లో రియల్-టైమ్ 3D గ్రాఫిక్స్ ప్రపంచంలో, అనేక డైనమిక్ లైట్లను రెండరింగ్ చేయడం ఎల్లప్పుడూ ఒక ముఖ్యమైన పనితీరు సవాలుగా ఉంది. డెవలపర్లుగా, మనం మరింత సుసంపన్నమైన, లీనమయ్యే దృశ్యాలను సృష్టించడానికి ప్రయత్నిస్తాము, కానీ ప్రతి అదనపు లైట్ సోర్స్ గణన వ్యయాన్ని విపరీతంగా పెంచుతుంది, WebGL ను దాని పరిమితులకు నెట్టివేస్తుంది. సాంప్రదాయ రెండరింగ్ టెక్నిక్లు తరచుగా ఒక కష్టమైన ఎంపికను బలవంతం చేస్తాయి: పనితీరు కోసం దృశ్య నాణ్యతను త్యాగం చేయడం, లేదా తక్కువ ఫ్రేమ్ రేట్లను అంగీకరించడం. కానీ ఈ రెండింటిలోనూ ఉత్తమమైనదాన్ని పొందే మార్గం ఉంటే ఎలా ఉంటుంది?
ఇక్కడే క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ వస్తుంది, దీనిని ఫార్వర్డ్+ అని కూడా పిలుస్తారు. ఈ శక్తివంతమైన టెక్నిక్ ఒక అధునాతన పరిష్కారాన్ని అందిస్తుంది, సాంప్రదాయ ఫార్వర్డ్ రెండరింగ్ యొక్క సరళత మరియు మెటీరియల్ ఫ్లెక్సిబిలిటీని డెఫర్డ్ షేడింగ్ యొక్క లైటింగ్ సామర్థ్యంతో మిళితం చేస్తుంది. ఇది ఇంటరాక్టివ్ ఫ్రేమ్ రేట్లను కొనసాగిస్తూనే వందలాది, లేదా వేలాది డైనమిక్ లైట్లతో దృశ్యాలను రెండర్ చేయడానికి మాకు అనుమతిస్తుంది.
ఈ వ్యాసం WebGL సందర్భంలో క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ యొక్క సమగ్ర అన్వేషణను అందిస్తుంది. మనం వ్యూ ఫ్రస్టమ్ను విభజించడం నుండి లైట్లను కల్లింగ్ చేయడం వరకు ముఖ్యమైన భావనలను విశ్లేషిస్తాము, మరియు అత్యంత క్లిష్టమైన ఆప్టిమైజేషన్పై తీవ్రంగా దృష్టి పెడతాము: లైట్ ఇండెక్సింగ్ డేటా పైప్లైన్. ఇది CPU నుండి GPU యొక్క ఫ్రాగ్మెంట్ షేడర్కు స్క్రీన్ యొక్క ఏ భాగాలు ఏ లైట్ల ద్వారా ప్రభావితమవుతాయో సమర్థవంతంగా తెలియజేసే యంత్రాంగం.
రెండరింగ్ ల్యాండ్స్కేప్: ఫార్వర్డ్ vs. డెఫర్డ్
క్లస్టర్డ్ రెండరింగ్ ఎందుకు అంత ప్రభావవంతంగా ఉందో అర్థం చేసుకోవడానికి, మనం ముందుగా దాని ముందున్న పద్ధతుల పరిమితులను అర్థం చేసుకోవాలి.
సాంప్రదాయ ఫార్వర్డ్ రెండరింగ్
ఇది అత్యంత సూటిగా ఉండే రెండరింగ్ విధానం. ప్రతి వస్తువు కోసం, వెర్టెక్స్ షేడర్ దాని వెర్టిసెస్ను ప్రాసెస్ చేస్తుంది, మరియు ఫ్రాగ్మెంట్ షేడర్ ప్రతి పిక్సెల్ కోసం చివరి రంగును లెక్కిస్తుంది. లైటింగ్ విషయానికి వస్తే, ఫ్రాగ్మెంట్ షేడర్ సాధారణంగా దృశ్యంలోని ప్రతి ఒక్క లైట్ ద్వారా లూప్ చేసి దాని సహకారాన్ని కూడబెట్టుకుంటుంది. దాని పేలవమైన స్కేలింగ్ దీనిలోని ప్రధాన సమస్య. గణన వ్యయం సుమారుగా (ఫ్రాగ్మెంట్ల సంఖ్య) x (లైట్ల సంఖ్య) కి అనులోమానుపాతంలో ఉంటుంది. కేవలం కొన్ని డజన్ల లైట్లతో, పనితీరు పడిపోవచ్చు, ఎందుకంటే ప్రతి పిక్సెల్ ప్రతి లైట్ను అనవసరంగా తనిఖీ చేస్తుంది, అవి మైళ్ల దూరంలో లేదా గోడ వెనుక ఉన్నప్పటికీ.
డెఫర్డ్ షేడింగ్
ఈ ఖచ్చితమైన సమస్యను పరిష్కరించడానికి డెఫర్డ్ షేడింగ్ అభివృద్ధి చేయబడింది. ఇది రెండు-పాస్ ప్రక్రియలో జ్యామితిని లైటింగ్ నుండి వేరు చేస్తుంది:
- జ్యామితి పాస్: దృశ్యం యొక్క జ్యామితి బహుళ ఫుల్-స్క్రీన్ టెక్స్చర్లలోకి రెండర్ చేయబడుతుంది, వీటిని సమిష్టిగా జి-బఫర్ అని పిలుస్తారు. ఈ టెక్స్చర్లు ప్రతి పిక్సెల్ కోసం పొజిషన్, నార్మల్స్ మరియు మెటీరియల్ లక్షణాలు (ఉదా., అల్బెడో, రఫ్నెస్) వంటి డేటాను నిల్వ చేస్తాయి.
- లైటింగ్ పాస్: ఒక ఫుల్-స్క్రీన్ క్వాడ్ గీయబడుతుంది. ప్రతి పిక్సెల్ కోసం, ఫ్రాగ్మెంట్ షేడర్ ఉపరితల లక్షణాలను పునర్నిర్మించడానికి జి-బఫర్ను శాంపిల్ చేస్తుంది మరియు ఆ తర్వాత లైటింగ్ను లెక్కిస్తుంది. దీనిలోని ముఖ్య ప్రయోజనం ఏమిటంటే, లైటింగ్ ప్రతి పిక్సెల్కు ఒక్కసారి మాత్రమే లెక్కించబడుతుంది, మరియు దాని వరల్డ్ పొజిషన్ ఆధారంగా ఏ లైట్లు ఆ పిక్సెల్ను ప్రభావితం చేస్తాయో సులభంగా నిర్ధారించవచ్చు.
అనేక లైట్లు ఉన్న దృశ్యాలకు ఇది అత్యంత సమర్థవంతమైనది అయినప్పటికీ, డెఫర్డ్ షేడింగ్కు దాని స్వంత ప్రతికూలతలు ఉన్నాయి, ముఖ్యంగా WebGL కోసం. జి-బఫర్ కారణంగా దీనికి అధిక మెమరీ బ్యాండ్విడ్త్ అవసరాలు ఉంటాయి, ట్రాన్స్పరెన్సీతో ఇబ్బంది పడుతుంది (దీనికి ప్రత్యేక ఫార్వర్డ్ రెండరింగ్ పాస్ అవసరం), మరియు MSAA వంటి యాంటీ-అలియాసింగ్ టెక్నిక్ల వాడకాన్ని క్లిష్టతరం చేస్తుంది.
మధ్యేమార్గం కోసం వాదన: ఫార్వర్డ్+
క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ ఒక సొగసైన రాజీని అందిస్తుంది. ఇది సింగిల్-పాస్ స్వభావాన్ని మరియు ఫార్వర్డ్ రెండరింగ్ యొక్క మెటీరియల్ ఫ్లెక్సిబిలిటీని నిలుపుకుంటుంది, కానీ ప్రతి ఫ్రాగ్మెంట్కు లైట్ లెక్కల సంఖ్యను నాటకీయంగా తగ్గించడానికి ఒక ప్రీ-ప్రాసెసింగ్ దశను పొందుపరుస్తుంది. ఇది భారీ జి-బఫర్ను నివారిస్తుంది, దీనిని మరింత మెమరీ-ఫ్రెండ్లీగా మరియు ట్రాన్స్పరెన్సీ మరియు MSAA తో వెంటనే అనుకూలంగా చేస్తుంది.
క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ యొక్క ముఖ్య భావనలు
క్లస్టర్డ్ రెండరింగ్ యొక్క కేంద్ర ఆలోచన ఏమిటంటే, మనం ఏ లైట్లను తనిఖీ చేయాలో మరింత తెలివిగా ఉండటం. ప్రతి పిక్సెల్ ప్రతి లైట్ను తనిఖీ చేయడానికి బదులుగా, స్క్రీన్లోని ఒక ప్రాంతాన్ని ప్రభావితం చేయడానికి ఏ లైట్లు తగినంత దగ్గరగా ఉన్నాయో మనం ముందుగానే నిర్ధారించవచ్చు మరియు ఆ ప్రాంతంలోని పిక్సెల్స్ కేవలం ఆ లైట్లను మాత్రమే తనిఖీ చేసేలా చేయవచ్చు.
కెమెరా యొక్క వ్యూ ఫ్రస్టమ్ను క్లస్టర్లు (లేదా టైల్స్) అని పిలువబడే చిన్న వాల్యూమ్ల 3D గ్రిడ్గా విభజించడం ద్వారా ఇది సాధించబడుతుంది.
మొత్తం ప్రక్రియను నాలుగు ప్రధాన దశలుగా విభజించవచ్చు:
- 1. క్లస్టర్ గ్రిడ్ సృష్టి: వ్యూ ఫ్రస్టమ్ను విభజించే 3D గ్రిడ్ను నిర్వచించి, నిర్మించడం. ఈ గ్రిడ్ వ్యూ స్పేస్లో స్థిరంగా ఉంటుంది మరియు కెమెరాతో పాటు కదులుతుంది.
- 2. లైట్ అసైన్మెంట్ (కల్లింగ్): గ్రిడ్లోని ప్రతి క్లస్టర్ కోసం, దాని ప్రభావ పరిధులు ఏయే లైట్లతో ఖండిస్తాయో ఆ లైట్ల జాబితాను నిర్ధారించడం. ఇది కీలకమైన కల్లింగ్ దశ.
- 3. లైట్ ఇండెక్సింగ్: ఇది మా దృష్టి కేంద్రం. మనం లైట్ అసైన్మెంట్ దశ ఫలితాలను ఒక కాంపాక్ట్ డేటా స్ట్రక్చర్లో ప్యాకేజ్ చేస్తాము, దానిని GPU కి సమర్థవంతంగా పంపవచ్చు మరియు ఫ్రాగ్మెంట్ షేడర్ ద్వారా చదవవచ్చు.
- 4. షేడింగ్: ప్రధాన రెండరింగ్ పాస్ సమయంలో, ఫ్రాగ్మెంట్ షేడర్ మొదట అది ఏ క్లస్టర్కు చెందిందో నిర్ధారిస్తుంది. ఆ తర్వాత అది ఆ క్లస్టర్కు సంబంధించిన లైట్ల జాబితాను తిరిగి పొందడానికి లైట్ ఇండెక్సింగ్ డేటాను ఉపయోగిస్తుంది మరియు లైటింగ్ లెక్కలను *కేవలం* ఆ చిన్న లైట్ల ఉపసమితికి మాత్రమే చేస్తుంది.
లోతైన విశ్లేషణ: క్లస్టర్ గ్రిడ్ నిర్మాణం
ఈ టెక్నిక్ యొక్క పునాది ఒక చక్కటి నిర్మాణాత్మక గ్రిడ్. ఇక్కడ తీసుకున్న ఎంపికలు కల్లింగ్ సామర్థ్యం మరియు పనితీరు రెండింటినీ నేరుగా ప్రభావితం చేస్తాయి.
గ్రిడ్ కొలతలను నిర్వచించడం
గ్రిడ్ దాని రిజల్యూషన్ను X, Y, మరియు Z అక్షాల వెంట నిర్వచిస్తుంది (ఉదా., 16x9x24 క్లస్టర్లు). కొలతల ఎంపిక ఒక ట్రేడ్-ఆఫ్:
- అధిక రిజల్యూషన్ (ఎక్కువ క్లస్టర్లు): మరింత కఠినమైన, మరింత కచ్చితమైన లైట్ కల్లింగ్కు దారితీస్తుంది. ప్రతి క్లస్టర్కు తక్కువ లైట్లు కేటాయించబడతాయి, అంటే ఫ్రాగ్మెంట్ షేడర్కు తక్కువ పని. అయినప్పటికీ, ఇది CPU లో లైట్ అసైన్మెంట్ దశ యొక్క ఓవర్హెడ్ను మరియు క్లస్టర్ డేటా స్ట్రక్చర్ల మెమరీ ఫుట్ప్రింట్ను పెంచుతుంది.
- తక్కువ రిజల్యూషన్ (తక్కువ క్లస్టర్లు): CPU-వైపు మరియు మెమరీ ఓవర్హెడ్ను తగ్గిస్తుంది కానీ ముతక కల్లింగ్కు దారితీస్తుంది. ప్రతి క్లస్టర్ పెద్దదిగా ఉంటుంది, కాబట్టి అది ఎక్కువ లైట్లతో ఖండిస్తుంది, ఫ్రాగ్మెంట్ షేడర్లో ఎక్కువ పనికి దారితీస్తుంది.
ఒక సాధారణ అభ్యాసం X మరియు Y కొలతలను స్క్రీన్ యాస్పెక్ట్ రేషియోతో ముడిపెట్టడం, ఉదాహరణకు, స్క్రీన్ను 16x9 టైల్స్గా విభజించడం. Z కొలత తరచుగా ట్యూన్ చేయడానికి అత్యంత క్లిష్టమైనది.
లాగరిథమిక్ Z-స్లైసింగ్: ఒక క్లిష్టమైన ఆప్టిమైజేషన్
మనం ఫ్రస్టమ్ యొక్క డెప్త్ (Z-యాక్సిస్) ను లీనియర్ స్లైస్లుగా విభజిస్తే, మనం పర్స్పెక్టివ్ ప్రొజెక్షన్కు సంబంధించిన సమస్యను ఎదుర్కొంటాము. కెమెరాకు దగ్గరగా భారీ పరిమాణంలో జ్యామితీయ వివరాలు కేంద్రీకృతమై ఉంటాయి, అయితే దూరంలో ఉన్న వస్తువులు చాలా తక్కువ పిక్సెల్లను ఆక్రమిస్తాయి. ఒక లీనియర్ Z-స్ప్లిట్ కెమెరాకు దగ్గరగా పెద్ద, కచ్చితత్వం లేని క్లస్టర్లను (ఎక్కడైతే కచ్చితత్వం ఎక్కువగా అవసరమో అక్కడ) మరియు దూరంలో చిన్న, వృధా అయిన క్లస్టర్లను సృష్టిస్తుంది.
దీనికి పరిష్కారం లాగరిథమిక్ (లేదా ఎక్స్పోనెన్షియల్) Z-స్లైసింగ్. ఇది కెమెరాకు దగ్గరగా చిన్న, మరింత కచ్చితమైన క్లస్టర్లను మరియు దూరంగా వెళ్లేకొద్దీ క్రమంగా పెద్ద క్లస్టర్లను సృష్టిస్తుంది, క్లస్టర్ పంపిణీని పర్స్పెక్టివ్ ప్రొజెక్షన్ పనిచేసే విధానంతో సమలేఖనం చేస్తుంది. ఇది ప్రతి క్లస్టర్కు మరింత ఏకరీతి సంఖ్యలో ఫ్రాగ్మెంట్లను నిర్ధారిస్తుంది మరియు మరింత సమర్థవంతమైన కల్లింగ్కు దారితీస్తుంది.
నియర్ ప్లేన్ `n` మరియు ఫార్ ప్లేన్ `f` ఇచ్చినప్పుడు, `N` మొత్తం స్లైస్ల నుండి i-వ స్లైస్ కోసం డెప్త్ `z` ను లెక్కించడానికి ఒక ఫార్ములాను ఇలా వ్యక్తీకరించవచ్చు:
z_i = n * (f/n)^(i/N)ఈ ఫార్ములా వరుస స్లైస్ డెప్త్ల నిష్పత్తి స్థిరంగా ఉండేలా నిర్ధారిస్తుంది, కావలసిన ఎక్స్పోనెన్షియల్ పంపిణీని సృష్టిస్తుంది.
విషయం యొక్క హృదయం: లైట్ కల్లింగ్ మరియు ఇండెక్సింగ్
ఇక్కడే మ్యాజిక్ జరుగుతుంది. మన గ్రిడ్ నిర్వచించబడిన తర్వాత, ఏ లైట్లు ఏ క్లస్టర్లను ప్రభావితం చేస్తాయో గుర్తించి, ఆ తర్వాత ఈ సమాచారాన్ని GPU కోసం ప్యాకేజ్ చేయాలి. WebGL లో, ఈ లైట్ కల్లింగ్ లాజిక్ సాధారణంగా లైట్లు లేదా కెమెరా కదిలిన ప్రతి ఫ్రేమ్లో జావాస్క్రిప్ట్ ఉపయోగించి CPU పై అమలు చేయబడుతుంది.
లైట్-క్లస్టర్ ఇంటర్సెక్షన్ టెస్ట్లు
ఈ ప్రక్రియ భావనాత్మకంగా సులభం: ప్రతి లైట్ ద్వారా లూప్ చేసి, ప్రతి క్లస్టర్ యొక్క బౌండింగ్ వాల్యూమ్తో ఇంటర్సెక్షన్ కోసం పరీక్షించడం. ఒక క్లస్టర్ యొక్క బౌండింగ్ వాల్యూమ్ కూడా ఒక ఫ్రస్టమ్. సాధారణ పరీక్షలలో ఇవి ఉంటాయి:
- పాయింట్ లైట్లు: గోళాలుగా పరిగణించబడతాయి. పరీక్ష ఒక స్పియర్-ఫ్రస్టమ్ ఇంటర్సెక్షన్.
- స్పాట్ లైట్లు: శంకువులుగా పరిగణించబడతాయి. పరీక్ష ఒక కోన్-ఫ్రస్టమ్ ఇంటర్సెక్షన్, ఇది మరింత సంక్లిష్టమైనది.
- డైరెక్షనల్ లైట్లు: ఇవి తరచుగా అన్నింటినీ ప్రభావితం చేస్తాయని పరిగణించబడతాయి, కాబట్టి అవి సాధారణంగా విడిగా నిర్వహించబడతాయి మరియు కల్లింగ్ ప్రక్రియలో చేర్చబడవు.
ఈ పరీక్షలను సమర్థవంతంగా అమలు చేయడం కీలకం. ఈ దశ తర్వాత, మనకు ఒక మ్యాపింగ్ ఉంటుంది, బహుశా జావాస్క్రిప్ట్ అర్రే ఆఫ్ అర్రేస్లో ఇలా ఉంటుంది: clusterLights[clusterId] = [lightId1, lightId2, ...].
డేటా స్ట్రక్చర్ సవాలు: CPU నుండి GPU కి
ఈ ప్రతి-క్లస్టర్ లైట్ జాబితాను మనం ఫ్రాగ్మెంట్ షేడర్కు ఎలా పంపుతాము? మనం వేరియబుల్-లెంగ్త్ అర్రేను పంపలేము. షేడర్కు ఈ డేటాను చూడటానికి ఒక ఊహించదగిన మార్గం అవసరం. ఇక్కడే గ్లోబల్ లైట్ లిస్ట్ మరియు లైట్ ఇండెక్స్ లిస్ట్ విధానం వస్తుంది. ఇది మన సంక్లిష్ట డేటా స్ట్రక్చర్ను GPU-ఫ్రెండ్లీ టెక్స్చర్లలోకి ఫ్లాట్ చేయడానికి ఒక సొగసైన పద్ధతి.
మనం రెండు ప్రాథమిక డేటా స్ట్రక్చర్లను సృష్టిస్తాము:
- ఒక క్లస్టర్ ఇన్ఫర్మేషన్ గ్రిడ్ టెక్స్చర్: ఇది ఒక 3D టెక్స్చర్ (లేదా 3D దానిని అనుకరించే 2D టెక్స్చర్), ఇక్కడ ప్రతి టెక్సెల్ మన గ్రిడ్లోని ఒక క్లస్టర్కు అనుగుణంగా ఉంటుంది. ప్రతి టెక్సెల్ రెండు ముఖ్యమైన సమాచారాలను నిల్వ చేస్తుంది:
- ఒక ఆఫ్సెట్: ఇది మన రెండవ డేటా స్ట్రక్చర్ (గ్లోబల్ లైట్ లిస్ట్) లోని ప్రారంభ ఇండెక్స్, ఇక్కడ ఈ క్లస్టర్ కోసం లైట్లు ప్రారంభమవుతాయి.
- ఒక కౌంట్: ఇది ఈ క్లస్టర్ను ప్రభావితం చేసే లైట్ల సంఖ్య.
- ఒక గ్లోబల్ లైట్ లిస్ట్ టెక్స్చర్: ఇది ఒక సాధారణ 1D జాబితా (2D టెక్స్చర్లో నిల్వ చేయబడింది), ఇది అన్ని క్లస్టర్ల కోసం అన్ని లైట్ ఇండిసెస్ యొక్క సంకలిత క్రమాన్ని కలిగి ఉంటుంది.
డేటా ఫ్లోను విజువలైజ్ చేయడం
ఒక సాధారణ దృష్టాంతాన్ని ఊహించుకుందాం:
- క్లస్టర్ 0 ఇండిసెస్ [5, 12] తో ఉన్న లైట్ల ద్వారా ప్రభావితమవుతుంది.
- క్లస్టర్ 1 ఇండిసెస్ [8, 5, 20] తో ఉన్న లైట్ల ద్వారా ప్రభావితమవుతుంది.
- క్లస్టర్ 2 ఇండెక్స్ [7] తో ఉన్న లైట్ ద్వారా ప్రభావితమవుతుంది.
గ్లోబల్ లైట్ లిస్ట్: [5, 12, 8, 5, 20, 7, ...]
క్లస్టర్ ఇన్ఫర్మేషన్ గ్రిడ్:
- క్లస్టర్ 0 కోసం టెక్సెల్:
{ ఆఫ్సెట్: 0, కౌంట్: 2 } - క్లస్టర్ 1 కోసం టెక్సెల్:
{ ఆఫ్సెట్: 2, కౌంట్: 3 } - క్లస్టర్ 2 కోసం టెక్సెల్:
{ ఆఫ్సెట్: 5, కౌంట్: 1 }
WebGL & GLSL లో అమలు చేయడం
ఇప్పుడు భావనలను కోడ్కు కనెక్ట్ చేద్దాం. అమలులో కల్లింగ్ మరియు డేటా తయారీ కోసం ఒక జావాస్క్రిప్ట్ భాగం, మరియు షేడింగ్ కోసం ఒక GLSL భాగం ఉంటాయి.
GPU కి డేటా బదిలీ (జావాస్క్రిప్ట్)
CPU లో లైట్ కల్లింగ్ చేసిన తర్వాత, మీకు మీ క్లస్టర్ గ్రిడ్ డేటా (ఆఫ్సెట్/కౌంట్ జతలు) మరియు మీ గ్లోబల్ లైట్ లిస్ట్ ఉంటాయి. వీటిని ప్రతి ఫ్రేమ్లో GPU కి అప్లోడ్ చేయాలి.
- క్లస్టర్ డేటాను ప్యాక్ చేసి అప్లోడ్ చేయండి: మీ క్లస్టర్ డేటా కోసం ఒక `Float32Array` లేదా `Uint32Array` ను సృష్టించండి. మీరు ప్రతి క్లస్టర్ కోసం ఆఫ్సెట్ మరియు కౌంట్ను ఒక టెక్స్చర్ యొక్క RG ఛానెల్లలోకి ప్యాక్ చేయవచ్చు. ఈ డేటాతో ఒక టెక్స్చర్ను సృష్టించడానికి `gl.texImage2D` లేదా అప్డేట్ చేయడానికి `gl.texSubImage2D` ను ఉపయోగించండి. ఇది మీ క్లస్టర్ ఇన్ఫర్మేషన్ గ్రిడ్ టెక్స్చర్ అవుతుంది.
- గ్లోబల్ లైట్ లిస్ట్ను అప్లోడ్ చేయండి: అదేవిధంగా, మీ లైట్ ఇండిసెస్ను ఒక `Uint32Array` లోకి ఫ్లాట్ చేసి, దానిని మరొక టెక్స్చర్కు అప్లోడ్ చేయండి.
- లైట్ ప్రాపర్టీలను అప్లోడ్ చేయండి: అన్ని లైట్ డేటా (పొజిషన్, రంగు, తీవ్రత, వ్యాసార్థం, మొదలైనవి) వేగవంతమైన, ఇండెక్స్డ్ లుకప్ల కోసం ఒక పెద్ద టెక్స్చర్ లేదా యూనిఫాం బఫర్ ఆబ్జెక్ట్ (UBO) లో నిల్వ చేయాలి.
ఫ్రాగ్మెంట్ షేడర్ లాజిక్ (GLSL)
ఫ్రాగ్మెంట్ షేడర్లో పనితీరు లాభాలు గ్రహించబడతాయి. ఇక్కడ దశల వారీ లాజిక్ ఉంది:
దశ 1: ఫ్రాగ్మెంట్ యొక్క క్లస్టర్ ఇండెక్స్ను నిర్ధారించడం
మొదట, ప్రస్తుత ఫ్రాగ్మెంట్ ఏ క్లస్టర్లో పడిందో తెలుసుకోవాలి. దీనికి దాని పొజిషన్ వ్యూ స్పేస్లో అవసరం.
// Uniforms providing grid information
uniform vec3 u_gridDimensions; // e.g., vec3(16.0, 9.0, 24.0)
uniform vec2 u_screenDimensions;
uniform float u_nearPlane;
uniform float u_farPlane;
// Function to get the Z-slice index from view-space depth
float getClusterZIndex(float viewZ) {
// viewZ is negative, make it positive
viewZ = -viewZ;
// The inverse of the logarithmic formula we used on the CPU
float slice = floor(log(viewZ / u_nearPlane) / log(u_farPlane / u_nearPlane) * u_gridDimensions.z);
return slice;
}
// Main logic to get the 3D cluster index
vec3 getClusterIndex() {
// Get X and Y index from screen coordinates
float clusterX = floor(gl_FragCoord.x / u_screenDimensions.x * u_gridDimensions.x);
float clusterY = floor(gl_FragCoord.y / u_screenDimensions.y * u_gridDimensions.y);
// Get Z index from fragment's view-space Z position (v_viewPos.z)
float clusterZ = getClusterZIndex(v_viewPos.z);
return vec3(clusterX, clusterY, clusterZ);
}
దశ 2: క్లస్టర్ డేటాను ఫెచ్ చేయడం
క్లస్టర్ ఇండెక్స్ను ఉపయోగించి, ఈ ఫ్రాగ్మెంట్ యొక్క లైట్ లిస్ట్ కోసం ఆఫ్సెట్ మరియు కౌంట్ను పొందడానికి మనం మన క్లస్టర్ ఇన్ఫర్మేషన్ గ్రిడ్ టెక్స్చర్ను శాంపిల్ చేస్తాము.
uniform sampler2D u_clusterTexture; // Texture storing offset and count
// ... in main() ...
vec3 clusterIndex = getClusterIndex();
// Flatten 3D index to 2D texture coordinate if needed
vec2 clusterTexCoord = ...;
vec2 lightData = texture2D(u_clusterTexture, clusterTexCoord).rg;
int offset = int(lightData.x);
int count = int(lightData.y);
దశ 3: లూప్ మరియు లైటింగ్ కూడబెట్టుట
ఇది చివరి దశ. మనం ఒక చిన్న, పరిమిత లూప్ను అమలు చేస్తాము. ప్రతి పునరావృతం కోసం, మనం గ్లోబల్ లైట్ లిస్ట్ నుండి ఒక లైట్ ఇండెక్స్ను ఫెచ్ చేస్తాము, ఆ తర్వాత ఆ ఇండెక్స్ను ఉపయోగించి లైట్ యొక్క పూర్తి లక్షణాలను పొంది, దాని సహకారాన్ని లెక్కిస్తాము.
uniform sampler2D u_globalLightIndexTexture;
uniform sampler2D u_lightPropertiesTexture; // UBO would be better
vec3 finalColor = vec3(0.0);
for (int i = 0; i < count; i++) {
// 1. Get the index of the light to process
int lightIndex = int(texture2D(u_globalLightIndexTexture, vec2(float(offset + i), 0.0)).r);
// 2. Fetch the light's properties using this index
Light currentLight = getLightProperties(lightIndex, u_lightPropertiesTexture);
// 3. Calculate this light's contribution
finalColor += calculateLight(currentLight, surfaceProperties, viewDir);
}
అంతే! వందల సార్లు నడిచే లూప్కు బదులుగా, ఇప్పుడు మనకు 5, 10, లేదా 30 సార్లు నడిచే లూప్ ఉంది, ఇది దృశ్యంలోని ఆ నిర్దిష్ట భాగంలో లైట్ సాంద్రతపై ఆధారపడి ఉంటుంది, ఇది ఒక అద్భుతమైన పనితీరు మెరుగుదలకు దారితీస్తుంది.
అధునాతన ఆప్టిమైజేషన్లు మరియు భవిష్యత్ పరిగణనలు
- CPU vs. కంప్యూట్: WebGL లో ఈ టెక్నిక్ యొక్క ప్రాథమిక ప్రతిబంధకం ఏమిటంటే, లైట్ కల్లింగ్ జావాస్క్రిప్ట్లో CPU పై జరుగుతుంది. ఇది సింగిల్-థ్రెడెడ్ మరియు ప్రతి ఫ్రేమ్లో GPU తో డేటా సింక్ అవసరం. WebGPU రాక ఒక గేమ్-ఛేంజర్. దాని కంప్యూట్ షేడర్లు మొత్తం క్లస్టర్ నిర్మాణం మరియు లైట్ కల్లింగ్ ప్రక్రియను GPU కి ఆఫ్లోడ్ చేయడానికి అనుమతిస్తాయి, దీనిని సమాంతరంగా మరియు అనేక రెట్లు వేగంగా చేస్తాయి.
- మెమరీ నిర్వహణ: మీ డేటా స్ట్రక్చర్లు ఉపయోగించే మెమరీ గురించి జాగ్రత్తగా ఉండండి. 16x9x24 గ్రిడ్ (3,456 క్లస్టర్లు) మరియు ప్రతి క్లస్టర్కు గరిష్టంగా 64 లైట్ల కోసం, గ్లోబల్ లైట్ లిస్ట్ సంభావ్యంగా 221,184 ఇండిసెస్ను కలిగి ఉండవచ్చు. మీ గ్రిడ్ను ట్యూన్ చేయడం మరియు ప్రతి క్లస్టర్కు వాస్తవిక గరిష్టాన్ని సెట్ చేయడం చాలా అవసరం.
- గ్రిడ్ను ట్యూన్ చేయడం: గ్రిడ్ కొలతల కోసం ఒక్క మ్యాజిక్ నంబర్ లేదు. ఆప్టిమల్ కాన్ఫిగరేషన్ మీ దృశ్యం యొక్క కంటెంట్, కెమెరా ప్రవర్తన, మరియు లక్ష్య హార్డ్వేర్పై ఎక్కువగా ఆధారపడి ఉంటుంది. అత్యుత్తమ పనితీరును సాధించడానికి విభిన్న గ్రిడ్ పరిమాణాలతో ప్రొఫైలింగ్ మరియు ప్రయోగాలు చేయడం చాలా ముఖ్యం.
ముగింపు
క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ కేవలం ఒక అకడమిక్ ఉత్సుకత మాత్రమే కాదు; ఇది రియల్-టైమ్ వెబ్ గ్రాఫిక్స్లో ఒక ముఖ్యమైన సమస్యకు ఒక ఆచరణాత్మక మరియు శక్తివంతమైన పరిష్కారం. వ్యూ స్పేస్ను తెలివిగా విభజించడం మరియు అత్యంత ఆప్టిమైజ్ చేయబడిన లైట్ కల్లింగ్ మరియు ఇండెక్సింగ్ దశను నిర్వహించడం ద్వారా, ఇది లైట్ కౌంట్ మరియు ఫ్రాగ్మెంట్ షేడర్ ఖర్చు మధ్య ప్రత్యక్ష సంబంధాన్ని విచ్ఛిన్నం చేస్తుంది.
సాంప్రదాయ ఫార్వర్డ్ రెండరింగ్తో పోలిస్తే ఇది CPU వైపు ఎక్కువ సంక్లిష్టతను పరిచయం చేసినప్పటికీ, పనితీరు ప్రయోజనం అపారమైనది, బ్రౌజర్లో నేరుగా మరింత సుసంపన్నమైన, డైనమిక్ మరియు దృశ్యపరంగా ఆకర్షణీయమైన అనుభవాలను సాధ్యం చేస్తుంది. దాని విజయం యొక్క సారం సమర్థవంతమైన లైట్ ఇండెక్సింగ్ పైప్లైన్లో ఉంది - సంక్లిష్టమైన ప్రాదేశిక సమస్యను GPU లో ఒక సాధారణ, పరిమిత లూప్గా మార్చే వంతెన.
వెబ్ ప్లాట్ఫాం WebGPU వంటి టెక్నాలజీలతో అభివృద్ధి చెందుతున్న కొద్దీ, క్లస్టర్డ్ ఫార్వర్డ్ రెండరింగ్ వంటి టెక్నిక్లు మరింత అందుబాటులోకి మరియు పనితీరుతో ఉంటాయి, నేటివ్ మరియు వెబ్-ఆధారిత 3D అప్లికేషన్ల మధ్య సరిహద్దులను మరింతగా చెరిపివేస్తాయి.