2 पॉइंट द्वारा GN⁺ 2025-02-08 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • सिर्फ 240 lines of GLSL code से Rick एनीमेशन बनाया जाता है, और बिना image या library के GPU shader हर pixel का रंग समय के अनुसार कैलकुलेट करता है
  • मुख्य बात यह है कि color_for_pixel(pixel, time) हर pixel पर चलती है, और signed distance field (SDF) shape के अंदर-बाहर और outline को distance value के रूप में व्यक्त करता है
  • चेहरा round_rect(), circle(), star(), bezier(), parabola() जैसे SDF shapes को move, scale, mirror, repeat और union करके बनाया गया है
  • बालों की wave, दांतों की repetition, होंठों की outline, blink, pupil movement, और सिर की हलचल को domain warping, mod(), sin(time), noise() से चरणबद्ध तरीके से जोड़ा गया है
  • final version में portal background भी शामिल है, लेकिन editor में video export नहीं है, इसलिए glslviewer और ffmpeg का अलग macOS workflow चाहिए

हर pixel का रंग कैलकुलेट करने वाला shader structure

  • एनीमेशन OpenGL Shading Language (GLSL) में लिखा गया है
  • पेज के अंदर live coding editor में example को तुरंत चलाया और बदला जा सकता है
  • basic entry point color_for_pixel(vec2 pixel, float time) function है
    • GPU preview के हर pixel के लिए इस function को चलाता है
    • यह function मूलतः यह जवाब देता है: “इस समय इस pixel का रंग क्या होना चाहिए?”
  • एक सरल example pixel.x, pixel.y, time values को color channel में डालकर pixel position और समय के बदलाव को visualize करता है
  • time आखिरी edit के बाद बीते हुए seconds हैं और लगातार बढ़ता रहता है

SDF से shape बनाने का तरीका

  • circle को length(pixel) से origin से pixel तक की दूरी निकालकर, फिर radius से compare करके बनाया जा सकता है
  • circle() function सिर्फ अंदर-बाहर बताने वाला bool नहीं, बल्कि boundary तक की दूरी लौटाता है
    • shape के अंदर negative
    • shape के बाहर positive
    • boundary के पास 0 के करीब value
  • इस तरह का function signed distance field (SDF) function कहलाता है
  • distance value का उपयोग करने से fill और outline दोनों को एक ही तरीके से handle किया जा सकता है
    • dist < -0.01 हो तो अंदर का रंग
    • dist < 0.0 हो तो काली outline
  • कई SDF shapes को min() से जोड़ा जा सकता है
    • अगर Rick के सिर और कान की outline अलग-अलग की जाए, तो उनके बीच अनचाही line बनती है
    • दोनों distance values को min() से जोड़ने पर सिर्फ सिर और कान की union outline बचती है
  • अन्य combination methods भी हैं, और reference के रूप में Inigo Quilez की 2D distance functions और primitive combination सामग्री लिंक की गई है

Rick के चेहरे को बनाने वाले shapes

  • सिर का आकार round_rect() से बनाया गया है, और कान भी अलग round_rect() से जोड़े गए हैं
  • season 1 poster की Rick image को preview पर blink की तरह overlap करके values मिलाई गईं
    • कई numerical values इसी trial-and-error process में मिलीं
    • color values image editor के eyedropper tool से ली गईं
  • आंखें circular SDF और 6-point star() से बनी हैं
    • eyeball को pixel.y *= .93 जैसे coordinate transform से vertical direction में थोड़ा stretch किया गया है
    • pupil को 6-point star SDF से छोटी distance value घटाकर corners को round किया गया है
  • दोनों आंखें code copy किए बिना pixel.x = abs(pixel.x) से left-right mirroring करके बनाई गई हैं
  • नाक, मुंह और eyebrows bezier() से बनाए गए हैं
  • बाल vertically stretched 11-point star() से शुरू होते हैं
  • shape draw करने वाले code का क्रम भी result को प्रभावित करता है
    • जो color पहले return होता है, वही सामने दिखने वाला shape बनता है
    • दांत और जीभ सिर्फ मुंह के shape के अंदर वाली condition में ही draw होते हैं, इसलिए वे मुंह के बाहर नहीं आते

बाल, दांत और decorative lines को refine करने की तकनीकें

  • सख्त star-shaped बालों को domain warping से wave जैसा बनाया गया है
    • domain warping pixel position पर आधारित random offset से coordinates को distort करने का तरीका है
    • एक ही position पर एक ही offset लागू होता है, इसलिए समय के साथ distortion consistent रह सकता है
    • संबंधित reference के रूप में Inigo Quilez का domain warping लिंक किया गया है
  • दांतों में parabola() SDF से एक shape बनाया जाता है और mod() से उसे horizontally repeat किया जाता है
    • mod(pixel.x, width) x coordinate को तय interval पर फिर 0 से शुरू करा देता है, जिससे वही shape repeat होता है
    • pixel.y = abs(pixel.y)-.06 से ऊपर-नीचे के दांत mirror किए जाते हैं
    • pow(pixel.x, 2.) आधारित y offset से दांतों को smile curve के अनुसार fit किया जाता है
    • abs(pixel.x+.06) < .194 जैसी conditions से infinite repetition सीमित की जाती है
  • होंठ और आंखों के नीचे की lines, SDF outline को बाहर की ओर shift करके बनाई जाती हैं
    • सामान्य outline: abs(distance_to_shape) < thickness
    • बाहर की ओर offset outline: abs(distance_to_shape - outset) < thickness
  • आंखों के नीचे की line में position condition जोड़कर उसे सिर्फ आंखों के नीचे के खास हिस्से में दिखाया जाता है

समय से movement बनाने का तरीका

  • सबसे सरल animation sin(time) को code में डालकर बनाई जाती है
    • sin() लगातार बढ़ते time को -1 से 1 के बीच wrap करके repeating animation बनाता है
    • sin(time)*.5 + .5 की तरह scale और offset से range adjust की जाती है
  • Rick के सिर का rotation, जीभ का rotation और eyebrow height इसी तरीके से animate होते हैं
    • rotation calculation के लिए rotateAt() function जोड़ा गया है
  • blink को समय के अनुसार draw होने वाले object को बदलने के तरीके से implement किया गया है
    • mod(time, 2.) < .09 हो तो बंद आंख draw होती है
    • नहीं तो खुली आंख और pupil draw होते हैं
  • pupils की irregular movement के लिए noise() का उपयोग किया गया है
    • ताकि आंखें लगातार smoothly move न करें, round(time) लगाने के बाद उसे noise() में दिया जाता है
  • बाल time domain warping से और flexible तरीके से move करते हैं
    • space को distort करने की बजाय time value को position के अनुसार delay किया जाता है
    • बालों के सिरों के करीब time delay बदलता है, इसलिए पूरा shape rigidly rotate नहीं करता बल्कि मुड़ता है

portal background और समापन

  • final version में Rick के पीछे portal effect जोड़कर काम पूरा किया गया है
  • portal effect ShaderToy user valena के effect पर आधारित है, और footnote में लिखा है कि यह FabriceNeyret2 का compact किया हुआ version है
  • code में performance से ज्यादा readability को प्राथमिकता दी गई है
  • पूरा result 2D shader animation के लिए जरूरी shape combination, SDF, warping, repetition, time-based transition, और noise-based movement को एक example में समेटता है

video export workflow

  • पेज का editor अभी animation को video के रूप में export नहीं कर सकता
  • अस्थायी तरीके के रूप में glslviewer और ffmpeg का macOS workflow दिया गया है
  • dependency install example इस प्रकार है
brew install glslviewer ffmpeg
  • export script एक temporary directory बनाती है और glslViewer को headless mode में चलाती है
    • resolution: 1920x1080
    • sequence: 0 सेकंड से 7 सेकंड तक
    • frame rate: 60
    • output file: animation.mp4
  • local live coding example में यह command उपयोग की जाती है
glslViewer shader.frag -w 575 -h 324 --noncurses -x 0 -y 0

super sampling और लेख की शुरुआत

  • shape edges smooth दिखने का कारण यह है कि पीछे super sampling लागू किया गया है
  • super sampling में स्क्रीन के एक pixel के भीतर 9 positions पर color_for_pixel() को call किया जाता है और उनका average दिखाया जाता है
  • #version 300 es इस्तेमाल करने पर editor का “pro” mode चालू होता है और automatic super sampling हट जाती है
  • लेख की शुरुआत 8 महीने पहले जारी किए गए वीडियो I Made a 3D Modeler, in C, in a Week से हुई थी
    • उस वीडियो में marching cubes algorithm समझाने वाला animation शामिल था
    • सामान्य animation software से इसे सटीक और तेज़ी से बनाना मुश्किल लगा, इसलिए इसे code में बनाना शुरू किया गया
    • बाद में लोगों ने animation बनाने की विधि पूछी, तो इसे इस लेख में संकलित किया गया

1 टिप्पणियां

 
GN⁺ 2025-02-08
Hacker News की राय
  • अगर आप दूसरा supersampling pass किए बिना स्मूथ anti-aliasing boundaries पाना चाहते हैं, तो SDF में standard derivatives का इस्तेमाल कर सकते हैं
    मूल रूप से step फ़ंक्शन को aastep से बदलना होता है: https://github.com/glslify/glsl-aastep
  • वाकई कमाल है। shader developers किसी और ही dimension के लगते हैं
    यह उस web, protocol और application development से बहुत अलग, बेहद सघन और iterative काम करने का तरीका है जिससे ज़्यादातर लोग परिचित होते हैं
    float का एक मान बदलकर shift-enter दबाते ही तुरंत नतीजा दिखना काफ़ी संतोषजनक है
    • सोच रहा हूँ कि क्या JavaScript canvas या उसके ऊपर p5.js जैसी abstraction के साथ काम करना भी ऐसा ही महसूस होता है
      समझ नहीं आ रहा कि यह graphics programming की बात सामान्य रूप से हो रही है, या खास तौर पर GPU shader work की
  • GLSL की शुरुआत के लिए यह बहुत अच्छी तरह से तैयार किया गया लेख है
    Inigo Quilez की एक संबंधित YouTube playlist भी है: https://www.youtube.com/watch?v=0ifChJ0nJfM&list=PL0EpikNmjs...
  • reference image को preview के ऊपर blink कराकर code edit करते समय original से तुलना करने का तरीका, hand-drawn animation में इस्तेमाल होने वाले तरीके से बिल्कुल वही है
    shader programming सच में एक अलग ही दुनिया है, और लेख भी बहुत शानदार है
  • GLSL की शुरुआत के लिए इसकी संरचना बहुत अच्छी थी
    सोच रहा हूँ कि इसे Vulkan या WebGPU/WebGL में करने का अनुभव कैसा होगा
    • लगभग वैसा ही। Vulkan और WebGL, दोनों में सीधे GLSL लिखा जा सकता है
      Vulkan में तकनीकी रूप से यह GLSL से SPIR-V तक जाने वाला रूप है
      browser में चलने वाला WebGPU तकनीकी रूप से सीधे GLSL इस्तेमाल नहीं कर सकता, लेकिन native WebGPU implementations GLSL ले सकती हैं, और conversion भी संभव है
      या फिर सीधे WGSL इस्तेमाल किया जा सकता है, जो C-जैसे syntax की जगह Rust से प्रेरित syntax होने को छोड़कर, GLSL से काफ़ी मिलता-जुलता है
  • इस animation को बनाने में 8 महीने लगे, यह जबरदस्त perseverance दिखाता है
  • जिज्ञासा है कि development process लगातार छोटे-छोटे values tweak करने वाली iteration थी, या आपने कोई खास editor इस्तेमाल किया था
    सही decimal values वाली 240 lines को trial and error से बनाना काफ़ी समय लेने वाला लगा होगा
    • बस उसी page में embedded code editor इस्तेमाल किया था
      binary search हाथ से भी तेज़ी से की जा सकती है
    • इस तरह के काम में आमतौर पर किसी slider या input value को लेकर उसे uniform से जोड़ते हैं
      uniform shader तक भेजा जाता है, और बिना recompile किए उसे update किया जा सकता है