ข้ามไปยังเนื้อหา
Tayakorn
← ห้องทดลองทั้งหมด
LAB.08กำลังทดลองเริ่ม

วาดภูเขาไฟด้วยสมการล้วน

ต่อยอดจาก LAB.06 วาดคลื่นทะเลด้วยสมการล้วน

ทดลองอะไร

รันสมการของภาพ Active Volcano ที่ Hamid Naderi Yeganeh เผยแพร่ไว้ด้วยตัวเอง แล้ววัดว่าได้ใกล้ต้นฉบับแค่ไหน ต่อจาก LAB.06 ที่ทำแบบเดียวกันกับภาพ Sea Wave ของเขา · สมการเขียนอยู่บนตัวภาพ จึงซูมอ่านทีละบรรทัดแล้วเขียนเป็นสคริปต์ numpy เรนเดอร์ 2000×1200 ตามพิกัดเดิม · สีของพิกเซลคือผลบวกของสี่พจน์ ได้แก่ ท้องฟ้า ภูเขา ฟ้าผ่า และเมฆ ส่วนเนื้อของเมฆกับลายบนผิวภูเขามาจากผลรวมคลื่น cosine 50 ชั้น · ตัวที่เล่นได้ในใบนี้คือ shader ที่แปลสมการชุดเดียวกันไปวาดบนการ์ดจอของเครื่องที่เปิดหน้า เปิด-ปิดทีละพจน์ได้ หมุนทิศและยืดระยะของผลต่างที่ทำให้เกิดแสงเงาได้ ปรับความชันของขั้นแสงเงาได้ และลดจำนวนพจน์ของผลรวมได้ · ภาพต้นฉบับเป็นงานของเขาจึงไม่ได้ลงไว้ที่นี่ ภาพทุกภาพในใบนี้วาดจากสมการชุดนี้

สมการเต็ม

ถอดจากสมการที่ Hamid Naderi Yeganeh เขียนไว้บนภาพ Active Volcano เรียงตามภาพต้นฉบับ จากสีของพิกเซลลงไปถึงชิ้นส่วนเล็กสุด ทุกตัวเป็นฟังก์ชันของ (x, y) จึงละ (x, y) ไว้ · เปลี่ยนแค่รูปการเขียนบางตัวให้อ่านง่าย เช่น 17^s/20^s เขียนเป็น (17/20)^s ค่าไม่เปลี่ยน

  1. pixel(m,n)=rgb(F(H0),F(H1),F(H2)),x=m1000600,y=601n600\operatorname{pixel}(m,n)=\operatorname{rgb}\big(F(H_0),\,F(H_1),\,F(H_2)\big),\qquad x=\frac{m-1000}{600},\quad y=\frac{601-n}{600}

    ภาพ 2000×1200 · m = 1…2000 คือคอลัมน์ n = 1…1200 คือแถว · สามช่องสีคือสมการเดียวกันที่ v = 0, 1, 2

  2. F(x)=255ee1000x|x|ee1000(x1)F(x)=\left\lfloor 255\,e^{-e^{-1000x}}\,|x|^{e^{-e^{1000(x-1)}}}\right\rfloor

    ตัวเดียวกับ F ตามภาพของ LAB.06 — ใบนี้ใช้ตัวนี้ตรง ๆ ทั้งในสคริปต์และในตัวที่เล่นได้

  3. Hv=2v2v+1220(1W1)(1110x25+y40)(1+3v213v+220ee3y+94)(1R)+W1(v2v+240+349v+3v240ee1000(T1T0))(1R)+(250+175(v2v))exp(e30|xy7+(y12)225+arccos(cos(35y+6x+2cos(15y7x)))50+arccos(cos(145y34x+2cos(30x+18y)))250|+3320+3100(6y72)10)+9+3B120(12v50+3v28v+1110ee500(C1C0))R\begin{aligned}H_v={}&\frac{2v^2-v+12}{20}\left(1-W_1\right)\left(\frac{11}{10}-\frac{x}{25}+\frac{y}{40}\right)\left(1+\frac{3v^2-13v+2}{20}\,e^{-e^{3y+\frac{9}{4}}}\right)\left(1-R\right)\\&+W_1\left(\frac{v^2-v+2}{40}+\frac{34-9v+3v^2}{40}\,e^{-e^{1000\left(T_1-T_0\right)}}\right)\left(1-R\right)\\&+\left(250+175\left(v^2-v\right)\right)\exp\!\left(-e^{30\left|x-\frac{y}{7}+\left(y-\frac{1}{2}\right)^2-\frac{2}{5}+\frac{\arccos(\cos(35y+6x+2\cos(15y-7x)))}{50}+\frac{\arccos(\cos(145y-34x+2\cos(30x+18y)))}{250}\right|+\frac{33}{20}+\frac{3}{100}\left(6y-\frac{7}{2}\right)^{10}}\right)\\&+\frac{9+3B_1}{20}\left(\frac{12-v}{50}+\frac{3v^2-8v+11}{10}\,e^{-e^{-500\left(C_1-C_0\right)}}\right)R\end{aligned}

    สี่พจน์เรียงตามบรรทัด: ท้องฟ้า · ภูเขา · ฟ้าผ่า · เมฆ · W, T, C, B ถูกใช้แค่ที่ v = 0 กับ 1 แล้วใช้ร่วมกันทั้งสามช่องสี · T₁ − T₀ กับ C₁ − C₀ คือผลต่างระหว่างจุดที่ห่างกันนิดเดียว = แสงเงาของภูเขาและเมฆ

  4. R=exp(e50(75+P1+12P14(Q1+P12)2A1B145)e1000(y+45x2))R=\exp\!\left(-e^{-50\left(\frac{7}{5}+P_1+\frac{1}{2}P_1^4-\left(Q_1+\frac{P_1}{2}\right)^2-A_1-\frac{B_1}{45}\right)}-e^{-1000\left(y+\frac{4}{5}-\frac{x}{2}\right)}\right)

    หน้ากากของเมฆ: 1 ในเมฆ 0 นอกเมฆ · แกนของเมฆคือเส้น Q + P/2 = 0 ที่เอนไปทางซ้ายเมื่อสูงขึ้น ขอบขรุขระมาจาก A กับ B

  5. Tv=s=150(1720)sexp(e510cos(120(65)s(cos(3s2)QvMv+2sin(3s2)Sv)+2cos(5s2))cos(120(65)s(sin(3s2)QvMv2cos(3s2)Sv)+2cos(7s2)))T_v=\sum_{s=1}^{50}\left(\frac{17}{20}\right)^s\exp\!\left(-e^{5-10\cos\left(\frac{1}{20}\left(\frac{6}{5}\right)^s\left(\cos(3s^2)\,Q_vM_v+2\sin(3s^2)\,S_v\right)+2\cos(5s^2)\right)\cos\left(\frac{1}{20}\left(\frac{6}{5}\right)^s\left(\sin(3s^2)\,Q_vM_v-2\cos(3s^2)\,S_v\right)+2\cos(7s^2)\right)}\right)

    ลายของผิวภูเขา — ภาพแสดงเฉพาะผลต่าง T₁ − T₀ ไม่เห็น T ตรง ๆ

  6. Av=s=150(87100)s(Kv,sLv,s)4A_v=\sum_{s=1}^{50}\left(\frac{87}{100}\right)^s\left(K_{v,s}L_{v,s}\right)^4
  7. Cv=5|75+Pv+Pv42(Qv+Pv2)2Av|C_v=\sqrt{5\left|\frac{7}{5}+P_v+\frac{P_v^4}{2}-\left(Q_v+\frac{P_v}{2}\right)^2-A_v\right|}

    ความสูงสมมติของเมฆ — นิพจน์ใต้รากคือนิพจน์เดียวกับใน R (ไม่มี B) ผลต่าง C₁ − C₀ จึงให้แสงเงาที่ตามรูปทรงของเมฆ

  8. Bv=s=1502(197200)s(310Kv,s6)(310Lv,s6)B_v=\sum_{s=1}^{50}2\left(\frac{197}{200}\right)^s\left(\frac{3}{10}-K_{v,s}^6\right)\left(\frac{3}{10}-L_{v,s}^6\right)
  9. Sv=Vv2+13Qv2S_v=\sqrt{V_v^2+\frac{1}{3}Q_v^2}
  10. Kv,s=cos(12(2320)s(cos(s2)Qv+32sin(s2)Pv)+12cos(32(2320)s(cos(2s2)Qv+32sin(2s2)Pv))+cos(7s2))K_{v,s}=\cos\left(\frac{1}{2}\left(\frac{23}{20}\right)^s\left(\cos(s^2)\,Q_v+\frac{3}{2}\sin(s^2)\,P_v\right)+\frac{1}{2}\cos\left(\frac{3}{2}\left(\frac{23}{20}\right)^s\left(\cos(2s^2)\,Q_v+\frac{3}{2}\sin(2s^2)\,P_v\right)\right)+\cos(7s^2)\right)

    K กับ L คือคลื่น cosine ที่หมุนทิศทุกชั้นและถี่ขึ้นชั้นละ 15% — ผลรวม 50 ชั้นของมันคือเนื้อของเมฆ

  11. Lv,s=cos(12(2320)s(32cos(s2)Pvsin(s2)Qv)+12cos(32(2320)s(cos(3s2)Qv+32sin(3s2)Pv))+cos(8s2))L_{v,s}=\cos\left(\frac{1}{2}\left(\frac{23}{20}\right)^s\left(\frac{3}{2}\cos(s^2)\,P_v-\sin(s^2)\,Q_v\right)+\frac{1}{2}\cos\left(\frac{3}{2}\left(\frac{23}{20}\right)^s\left(\cos(3s^2)\,Q_v+\frac{3}{2}\sin(3s^2)\,P_v\right)\right)+\cos(8s^2)\right)
  12. Wv=exp(e1000Vv1000(7+800|20Qv7|322032)18cos2(32Pv19Qvcos(12Pv+7Qv))4cos2(82Pv+49Qv2cos(32Pv27Qv))+400+1000023|Qv|25)W_v=\exp\!\left(-e^{1000V_v-1000\left(7+800\left|20Q_v-7\right|^{\frac{3}{2}}20^{-\frac{3}{2}}\right)^{-1}-8\cos^2\left(32P_v-19Q_v-\cos(12P_v+7Q_v)\right)-4\cos^2\left(82P_v+49Q_v-2\cos(32P_v-27Q_v)\right)+400+\frac{10000}{23}\left|Q_v\right|^{\frac{2}{5}}}\right)

    หน้ากากของภูเขา · |Q_v|^(2/5) ทำยอดแหลมที่ x = 0 · พจน์ (7 + 800|20Q_v − 7|^(3/2) 20^(−3/2))^(−1) คือยอดเล็กอีกยอดทางขวาที่ x = 0.35

  13. Mv=5052(23|5Vv+2|)52M_v=50^{\frac{5}{2}}\left(23\left|5V_v+2\right|\right)^{-\frac{5}{2}}

    โตไม่จำกัดเมื่อ 5V + 2 เข้าใกล้ 0 · เส้นที่ Q·M คงที่ทุกเส้นวิ่งเข้าหาจุด Q = 0, V = −0.4 ตรงยอดภูเขาไฟ ลายของ T จึงลู่เข้าหายอด

  14. Vv=Pv120cos4(4Qv+3Pv+cos(2Qv+Pv))9500cos2(14Pv8Qv+cos(5Pv3Qv))V_v=P_v-\frac{1}{20}\cos^4\left(4Q_v+3P_v+\cos(2Q_v+P_v)\right)-\frac{9}{500}\cos^2\left(14P_v-8Q_v+\cos(5P_v-3Q_v)\right)
  15. Pv=y+v1000P_v=y+\frac{v}{1000}
  16. Qv=x+v500Q_v=x+\frac{v}{500}

    ช่อง v = 1 เลื่อนจุดไป (Δx, Δy) = (0.002, 0.001) — ระยะนี้คือต้นทางของแสงเงาทั้งภาพ

ค่าที่เผยแพร่
สี่พจน์ของ H

สีของพิกเซลคือผลบวกของสี่พจน์ ปิดพจน์ไหน พจน์นั้นเป็นศูนย์ · ท้องฟ้ากับภูเขาถูกคูณด้วย (1 − R) ปิดเมฆแล้วจึงเหลือช่องดำเป็นรูปเมฆ

แสงเงาจากผลต่าง

ช่อง v = 1 เลื่อนจุด (x, y) ไปนิดเดียว ผลต่าง T₁ − T₀ กับ C₁ − C₀ จึงเป็นความชันตามทิศนั้น แล้ว e^(−e^(kx)) แปลงเป็นด้านสว่างกับด้านมืด · หมุนทิศ = ย้ายแสง · ระยะ 0 = ไม่มีผลต่าง ลายบนภูเขาหายทั้งผืน

(Δx, Δy) = (0.0020, 0.0010)
×1.00
×1.00
k = 1000 · 500
รายละเอียดจากผลรวม

T, A และ B คือผลรวมของคลื่น cosine 50 ชั้นที่ถี่ขึ้นทีละชั้น · รวมน้อยพจน์ ขอบเมฆกับผิวภูเขาเรียบลง และรูปทรงของเมฆเปลี่ยนด้วย เพราะ A กับ B อยู่ในหน้ากาก R · พจน์เหล่านี้คือต้นทุนหลักของภาพ ดูเวลาวาดใต้ภาพ

50/50
LAB.08 — shader แปลสมการชุดเดียวกับ render.py มารันบนการ์ดจอของเครื่องที่เปิดหน้านี้ · เปิดมาที่ค่าที่เผยแพร่ทุกตัว · วัดบนการ์ดจอในตัวของเครื่องที่เขียน: ที่ 2000×1200 ค่าสีตรงกับ render.py เป๊ะ 99.62% ของพิกเซล ที่เหลือต่างไม่เกิน 1 ต่อช่องสี · ภาพต้นฉบับไม่ได้อยู่ในหน้านี้ จึงไม่มีตัวเลขว่าเหมือนต้นฉบับกี่ % แบบสด

ได้เรียนรู้อะไร

ข้อที่ได้คือกลไกของแสงเงา ไม่ใช่ตัวเลข · ในสมการไม่มีแหล่งแสงและไม่มีเวกเตอร์แนวฉาก (normal) สักตัว มีแค่ช่อง v = 1 ที่เลื่อนจุด (x, y) ไป (0.002, 0.001) ผลต่าง T₁ − T₀ กับ C₁ − C₀ จึงเป็นความชันของพื้นผิวตามทิศนั้นแบบผลต่างจำกัด แล้ว e^(−e^(kx)) แปลงความชันเป็นด้านสว่างกับด้านมืด · ยันด้วยการเล่นกับมันตรง ๆ: ตั้งระยะเป็น 0 แล้วลายบนภูเขาหายทั้งผืนเหลือสีเรียบสีเดียว แปลว่าลายทั้งหมดบนภูเขาคือแสงเงาของพื้นผิว T ที่ภาพไม่เคยวาดออกมาตรง ๆ · หมุนทิศ 180° แล้วด้านสว่างกับด้านมืดสลับกัน · และขั้นนี้ไม่ได้คมแบบขั้นบันไดของ LAB.06: ค่ากลางของ |k · ผลต่าง| อยู่ราว 0.9 บนภูเขาและ 2.9 ในเมฆ ตกอยู่ช่วงโค้งของ e^(−e^z) ไม่ใช่หน้าผา แสงเงาจึงไล่น้ำหนักได้ ไม่ได้ออกมาเป็นขาวกับดำ · ย้ายขึ้นการ์ดจอแล้ว ที่ 2000×1200 ค่าสีตรงกับ numpy เป๊ะ 99.62% ของพิกเซล ที่เหลือต่างไม่เกิน 1 ต่อช่องสี ใช้เวลาราว 30–40 ms ต่อภาพบนการ์ดจอในตัวของเครื่องที่เขียน (ภาพแรกราว 165 ms · numpy ใช้ 65 วินาที) · ค่าคงที่ที่ขึ้นกับ s ล้วน ๆ คำนวณไว้ก่อนฝั่ง JavaScript ด้วย float64 แล้วส่งเข้าไปเป็นตาราง การ์ดจอจึงไม่ต้องคิด cos ของมุมใหญ่อย่าง 7s² ที่ s = 50 (คือ cos(17,500)) ด้วย float32 เอง

ที่ยังไม่ถึง

เทียบกับต้นฉบับพิกเซลต่อพิกเซลด้วยเกณฑ์เดียวกับ LAB.06 (ต่างรวมสามช่องสีไม่เกิน 60 จาก 765) ตรงกัน 94.1% อีก 5.9% ไม่ตรง ความต่างเฉลี่ย 16 จาก 765 · ท้องฟ้าตรง 99.7% แต่เนื้อเมฆตรง 88.9% ผิวภูเขา 88.5% และฟ้าผ่า 72.3% · ส่วนที่ไม่ตรงอยู่ที่ระดับ 1–2 พิกเซลทั้งหมด: ย่อทั้งสองภาพลงสองเท่าตรงกัน 99.49% ย่อสี่เท่าตรงกัน 99.99% และเลื่อนภาพไปหนึ่งพิกเซลในทิศใดก็ตรงน้อยลงเหลือไม่เกิน 87.8% แปลว่าตำแหน่งตรงกันแล้ว · แต่ยังแยกไม่ได้ว่าส่วนที่ต่างมาจากอะไร: ภาพต้นฉบับที่มีคือ JPEG ที่ถูกขยายมา 1.024 เท่า และแค่เอาภาพที่รันได้เองผ่านทางเดียวกันแบบเดาเอา (ขยาย 1.024 เท่าแล้วบีบเป็น JPEG) ก็ทำให้พิกเซลของตัวเองหลุดเกณฑ์ไปแล้ว 0.8–2.9% แล้วแต่วิธีขยายที่เดา ส่วนที่เหลืออาจมาจากวิธีขยายจริงหรือโปรแกรมที่เขาใช้คิดเลข ซึ่งไม่รู้ทั้งคู่ · ตัวที่เล่นได้วัดบนการ์ดจอเครื่องเดียว มือถือกับการ์ดจอยี่ห้ออื่นยังไม่ได้วัดทั้งความตรงและความเร็ว · และไม่ได้ดัดค่าคงที่ให้ภาพเหมือนขึ้น เพราะใบนี้วัดสมการที่เผยแพร่ ไม่ใช่ฟิตภาพ

สายการทดลอง

  1. LAB.06 วาดคลื่นทะเลด้วยสมการล้วน
  2. LAB.08 วาดภูเขาไฟด้วยสมการล้วน (ใบนี้)