1 điểm bởi GN⁺ 2 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Shader trình duyệt kết hợp tán xạ Rayleigh, tán xạ Mie và hấp thụ ozone để kết xuất bầu trời xanh cùng hoàng hôn·bình minh theo thời gian thực
  • Tích lũy độ sâu quang học của tia camera và độ truyền qua theo định luật Beer, rồi tính phân bố tán xạ theo hướng Mặt Trời bằng hàm pha
  • Hiệu ứng hoàng hôn thực hiện thêm một light-march riêng theo hướng Mặt Trời tại mỗi mẫu để phản ánh lượng ánh sáng Mặt Trời bị mất khi đi qua khí quyển
  • Shader bầu trời phẳng trở thành một hiệu ứng hậu xử lý nhờ depth buffer và khôi phục tọa độ thế giới, nên có thể xử lý cả sương mù khí quyển giữa các vật thể trong cảnh
  • Ở quy mô hành tinh, kỹ thuật này được mở rộng với logarithmic depth buffer, ray-sphere intersection và LUT cho Transmittance·Sky-view·Aerial Perspective

Mục tiêu và tài liệu tham khảo của shader tán xạ khí quyển

  • Mục tiêu là tái hiện bằng shader trình duyệt các lớp chuyển tiếp từ màu cam sẫm·màu xanh lam·màu đen của nền không gian trong tầng khí quyển trên của Trái Đất, giống như ảnh hoàng hôn quỹ đạo thấp do tàu con thoi Endeavour chụp
  • Phạm vi triển khai bắt đầu từ sky dome chân thực kết hợp raymarching, tán xạ Rayleigh, tán xạ Mie và hấp thụ ozone, rồi mở rộng sang lớp vỏ khí quyển quanh hành tinh và tối ưu hóa dựa trên LUT
  • Tài liệu tham khảo chính là Three Geospatial, A Scalable and Production Ready Sky and Atmosphere Rendering Technique của Sébastien Hillaire, và Atmospheric Scattering (and also just faking it)

Mô hình cơ bản của kết xuất bầu trời

  • Vì sao gradient đơn giản là không đủ

    • Màu bầu trời không chỉ là một nền xanh đơn giản mà phải được xử lý như kết quả của việc ánh sáng tương tác với không khí và các thành phần của nó
    • Cần xét đến các biến như độ cao của người quan sát, lượng bụi và thời điểm trong ngày, và phép tính diễn ra bên trong một thể tích (volume)
  • Lấy mẫu mật độ khí quyển

    • Khí quyển được lấy mẫu bằng raymarching tương tự như volumetric clouds hay volumetric light
    • Từ vị trí camera, bắn một tia và tiến dần qua môi trường trong suốt; trong quá trình đó tính độ truyền qua (transmittance) của phần ánh sáng còn sống sót sau khi đi qua khí quyển, cùng với tán xạ (scattering) được đổi hướng về phía camera tại mỗi mẫu
    • Có thể tham khảo Painting with Math: A Gentle Study of Raymarching để ôn lại raymarching
  • Mật độ Rayleigh và độ sâu quang học

    • Để tính độ truyền qua, cần tích lũy mật độ khí quyển mà tia gặp trên đường đi để tính độ sâu quang học (optical depth)
    • Hàm mật độ Rayleigh biểu diễn lượng “không khí” có ở độ cao h, phản ánh hiệu ứng khí quyển loãng dần khi độ cao tăng lên
    • Ví dụ triển khai dùng RAYLEIGH_SCALE_HEIGHT = 8.0km, ATMOSPHERE_HEIGHT = 100.0km, VIEW_DISTANCE = 200.0km, PRIMARY_STEPS = 24
    • rayleighDensity(h)exp(-max(h, 0.0) / RAYLEIGH_SCALE_HEIGHT), và trong vòng lặp nó được tích lũy bằng viewOpticalDepth += dR * stepSize
  • Định luật Beer và màu xanh của bầu trời ban ngày

    • Từ độ sâu quang học, tính độ truyền qua T tại một điểm cụ thể; T=1.0 nghĩa là không mất ánh sáng, còn T=0.0 nghĩa là ánh sáng đã biến mất hoàn toàn
    • Độ truyền qua được tính theo định luật Beer, ví dụ mã dùng vec3 transmittance = exp(-rayleighBeta * viewOpticalDepth)
    • rayleighBeta là hệ số tán xạ Rayleigh, được lưu trong shader dưới dạng vec3(0.0058, 0.0135, 0.0331)
    • Góc giữa hướng ánh sáng Mặt Trời và tia nhìn được mô hình hóa bằng hàm pha Rayleigh có dạng 3.0 / (16.0 * PI) * (1.0 + mu * mu)
    • Do hệ số tán xạ Rayleigh, màu đỏ gần như không bị tán xạ, màu xanh lục bị tán xạ nhiều hơn một chút, còn màu xanh lam bị tán xạ mạnh nhất nên bầu trời ban ngày trông có màu xanh
    • Khi mở rộng thành một tia cho mỗi pixel, phía đường chân trời sẽ đi qua nhiều khí quyển hơn nên trông như lớp sương trắng sáng, còn khi độ cao tăng lên thì màu chuyển thành xanh đậm và tối hơn

Tán xạ Mie và hấp thụ ozone

  • Những hiệu ứng mà Rayleigh thôi là chưa đủ

    • Chỉ với tán xạ Rayleigh cũng có thể cho kết quả khá ổn, nhưng một bầu trời chân thực hơn cần thêm các hiệu ứng khí quyển khác
    • Tán xạ Mie biểu diễn tương tác giữa ánh sáng với các hạt lớn hơn như bụi hay aerosol, và có hàm mật độ cùng hàm pha biểu diễn sự phân phối lại theo từng hướng
    • Hấp thụ ozone loại bỏ một số bước sóng ánh sáng trên đường đi qua tầng khí quyển trên thay vì tán xạ chúng
    • Hấp thụ ozone đặc biệt làm màu trời sâu hơn và dịch chuyển màu ở đường chân trời, lúc hoàng hôn, bình minh và thời kỳ chạng vạng quanh đó
  • Tích lũy Mie và ozone

    • Triển khai dùng đồng thời Rayleigh, Mie và ozone sẽ tích lũy độ sâu quang học riêng của từng thành phần bằng viewODR, viewODM, viewODO
    • Ở mỗi mẫu, tính dR = rayleighDensity(h), dM = mieDensity(h), dO = ozoneDensity(h), rồi tạo tau từ tổng của BETA_R * viewODR, BETA_M_EXT * viewODM, BETA_OZONE_ABS * viewODO
    • Độ truyền qua được tính bằng exp(-tau), còn sumR, sumM, sumO sẽ tích lũy tương ứng mật độ, độ truyền qua và stepSize
    • Tán xạ cuối cùng được tính theo dạng SUN_INTENSITY * (phaseR * BETA_R * sumR + phaseM * BETA_M_SCATTER * sumM + BETA_OZONE_SCATTER * sumO)
  • Các hằng số chính và hiệu ứng

    • MIE_SCALE_HEIGHT là biến tương ứng với RAYLEIGH_SCALE_HEIGHT dành cho aerosol; vì các hạt này thường tập trung gần đường chân trời nên nó được đặt nhỏ hơn, ở mức 1.2km
    • MIE_BETA_SCATTER kiểm soát mức độ các hạt tán xạ ánh sáng về phía camera; vì phần lớn gần như không phụ thuộc bước sóng nên nó được đặt là vec3(0.003)
    • MIE_BETA_EXT là hệ số suy hao Mie biểu diễn lượng ánh sáng bị loại khỏi đường đi, khiến khí quyển xa trông mờ đục hơn
    • MIE_G điều khiển tính bất đẳng hướng; 0.0 là tán xạ đồng đều, còn 1.0 nghĩa là thiên mạnh hơn về tán xạ hướng tới trước
    • OZONE_BETA_ABS có giá trị vec3(0.00065, 0.00188, 0.00008), hấp thụ nhiều hơn các dải xanh lục và vàng-cam, làm màu trời dịch về phía xanh lam·đỏ·tím
    • Khi tích hợp Mie và ozone, màu “sky blue” trở nên tự nhiên hơn và xuất hiện quầng sáng mờ quanh Mặt Trời; hiệu ứng tán xạ Mie cũng rõ hơn khi Mặt Trời ở gần đường chân trời

Đường đi ánh sáng và hoàng hôn·bình minh

  • Giới hạn của cách triển khai hiện có

    • sky fragment shader có thể kết xuất màu sắc tự nhiên ở nhiều độ cao khác nhau và phản ánh các mô hình truyền qua của Mie, Rayleigh và ozone
    • Tuy nhiên, ngay cả khi đưa mặt trời xuống gần đường chân trời, vẫn chỉ xuất hiện quầng sáng trắng mờ đục mà không có suy giảm ánh sáng hay hiệu ứng hoàng hôn·bình minh
    • Nguyên nhân là vì vòng lặp raymarching hiện tại chỉ tính suy giảm ánh sáng trên tia nhìn từ camera đến từng mẫu
    • Cũng cần tính cả mức thất thoát của ánh sáng mặt trời khi đi qua khí quyển trước khi đến điểm mẫu
  • Vòng lặp lồng nhau light-march

    • Tại mỗi điểm mẫu, chạy một vòng lặp lồng nhau riêng theo hướng nguồn sáng để lấy mẫu độ truyền qua của đường đi đó
    • Cách tiếp cận liên quan cũng được dùng trong real-time cloudscapesvolumetric lighting
    • lightMarch(float start, float sunY) lặp LIGHTMARCH_STEPS lần và tích lũy odR, odM, odO
    • Cộng thêm độ sâu quang học theo hướng mặt trời sunOD vào các độ sâu quang học hiện có viewODR, viewODM, viewODO
    • tau cuối cùng được tạo bằng cách cộng BETA_R * (viewODR + sunOD.x), BETA_M_EXT * (viewODM + sunOD.y), BETA_OZONE_ABS * (viewODO + sunOD.z)
    • Với cách triển khai này, có thể kết xuất bầu trời cho hoàng hôn, bình minh, mặt trời ở thiên đỉnh và các điều kiện chiếu sáng ở giữa
    • Uniform sun angle tạo ra sự thay đổi sắc xanh của bầu trời trong suốt cả ngày, còn tán xạ Mie giúp hòa ánh sáng vào đường chân trời một cách tự nhiên lúc hoàng hôn và bình minh
    • Khi mặt trời ở thấp, ozone thêm tông tím cho bầu trời

Mở rộng sang khí quyển hành tinh

  • Từ nền phẳng sang hiệu ứng hậu xử lý

    • Shader đã tạo ở trên cung cấp một nền trời tốt, nhưng vẫn gần với một nền phẳng trong cảnh React Three Fiber
    • Bước tiếp theo là biến nó thành hiệu ứng hậu xử lý (post-processing effect) để kết xuất thể tích có xét đến độ sâu của cảnh và lớp vỏ khí quyển bao quanh mesh hành tinh
    • Để làm vậy, tái tạo tọa độ không gian thế giới từ tọa độ screenUV và phản ánh depth buffer của cảnh vào raymarching
  • Tái tạo không gian thế giới và tia 3D

    • Để áp dụng tán xạ khí quyển vào cảnh, không chỉ vẽ bầu trời mà còn phải lấp đầy không gian giữa camera và các đối tượng đã được kết xuất trên màn hình
    • Dữ liệu cần thiết là depth buffer của cảnh, projectionMatrixInverse, matrixWorld, position của camera, và các giá trị này được truyền vào uniform của hiệu ứng hậu xử lý
    • getWorldPosition(vec2 uv, float depth) tạo clipZ bằng depth * 2.0 - 1.0, tạo tọa độ NDC bằng uv * 2.0 - 1.0, rồi áp dụng projectionMatrixInverseviewMatrixInverse
    • Quy trình tương tự cũng được dùng trong hiệu ứng hậu xử lý volumetric lighting của On Shaping Light
    • Sau khi lấy được worldPosition của pixel hiện tại, đặt rayOrigin là vị trí camera, còn rayDir được tính bằng normalize(worldPosition - rayOrigin) để tiến dọc theo tia 3D cho từng pixel trên màn hình
  • Điều chỉnh đoạn raymarch bằng depth buffer

    • Để xét đến hình học của cảnh, cần dùng depth buffer để xác định đoạn raymarch của tia hiện tại thay cho stepSize cố định
    • Lấy độ sâu của cảnh trên tia bằng sceneDepth = depthToRayDistance(uv, depth)
    • Pixel nền được xác định bằng depth >= 1.0 - 1e-7, và với “sky pixels” thì áp dụng sceneDepth = atmosphereHeight * SKY_MARCH_DISTANCE_MULTIPLIER
    • Nếu tia hướng xuống dưới, tính giao cắt mặt đất bằng tGround = observerAltitude / max(-rayDir.y, 1e-4) và giới hạn bằng rayEnd = min(rayEnd, tGround)
    • stepSize cuối cùng được tính bằng (rayEnd - rayStart) / float(PRIMARY_STEPS)
    • Các tia chạm vào vật thể gần hoặc mặt đất sẽ được lấy mẫu chính xác hơn với stepSize nhỏ, còn các tia đi xa thì phân bố cùng số lượng mẫu trên quãng đường dài hơn
  • Sương mù khí quyển trong cảnh

    • Shader được triển khai dưới dạng hiệu ứng hậu xử lý sẽ áp dụng tán xạ khí quyển lên toàn bộ thể tích của cảnh, đồng thời có thể dùng sky shader làm nền và vẫn xét đến hình học của cảnh
    • Các vật thể gần camera sẽ hiện rõ hơn, còn các vật thể ở xa sẽ bị mờ nhiều hơn
    • Có thể xem ví dụ tương tác thêm thiên thể có thể kéo bằng Raycaster trong tweet của MaximeHeckel

Kết xuất hành tinh

  • Hai bước cần thiết

    • Để kết xuất bầu khí quyển chân thực quanh hành tinh, cần một logarithmic depth buffer để xử lý quy mô lớn, cùng một lớp vỏ khí quyển hình cầu xác định nơi tia sáng bắt đầu và kết thúc trong khí quyển
  • logarithmic depth buffer

    • Ở quy mô hành tinh, khi nhìn từ xa, shader có thể khó phân biệt chênh lệch độ sâu giữa khí quyển và vỏ hành tinh, dẫn đến depth fighting
    • Vì độ cao khí quyển chỉ vài km, cần điều chỉnh cả cách định nghĩa depth buffer của cảnh lẫn cách đọc nó trong các hiệu ứng hậu xử lý
    • Trong prop gl bao quanh Canvas của React Three Fiber, đặt logarithmicDepthBuffer: true
    • Cấu hình ví dụ có dạng <Canvas shadows gl={{ alpha: true, logarithmicDepthBuffer: true }}>
    • Trong shader, tính toán sceneDepth được định nghĩa lại để chuyển logarithmic depth buffer về khoảng cách dọc theo tia
    • logDepthToViewZ(depth) dùng pow(2.0, depth * log2(cameraFar + 1.0)) - 1.0 và trả về -d
  • Tìm đoạn khí quyển bằng ray-sphere intersection

    • Dùng ray-sphere intersection test để tìm điểm tia nhìn đi vào và đi ra khỏi khối cầu khí quyển (atmospheric sphere)
    • Khi có hai giao điểm, có thể tránh lãng phí mẫu ở ngoài khí quyển và chỉ giới hạn vòng lặp raymarching trong đoạn đó
    • Vì hành tinh là mesh hình cầu và được bao quanh bởi một khối cầu khí quyển lớn hơn một chút, cùng phép kiểm tra giao cắt đó cũng được áp dụng cho chính hành tinh
    • Nếu tia chạm mặt đất trước khi thoát khỏi khí quyển, dùng giao điểm với mặt đất làm điểm kết thúc cho đoạn raymarching
    • Phần cài đặt raySphereIntersect được dùng tham khảo từ Ray-Surface intersection functions của Inigo Quilez
  • Điều kiện kết thúc khí quyển và object trong cảnh

    • Khí quyển phải kết thúc khi chạm bề mặt hành tinh, hoặc khi gặp object khác trong cảnh trước khi chạm đất
    • Với trường hợp chạm hành tinh, mặc định dừng ở mặt đất bằng atmosphereFar = min(atmosphereFar, planetHit.x)
    • Nếu mesh khác được render phía trước mặt đất, xác định bằng điều kiện sceneDepth < planetHit.x - 2.0 rồi áp dụng atmosphereFar = min(atmosphereFar, sceneDepth)
    • Nếu không có logic này, sẽ phát sinh vấn đề bề mặt hành tinh xuất hiện phía trước object
  • Demo React Three Fiber và các glitch còn lại

    • Sau khi phản ánh hai điều chỉnh này vào mã, có thể triển khai tán xạ khí quyển như một hiệu ứng hậu xử lý và kết xuất khí quyển quanh hành tinh
    • Cảnh demo trong React Three Fiber kết xuất một “Sun - Earth system” đơn giản và áp dụng hiệu ứng tùy chỉnh
    • Khi điều chỉnh vị trí mặt trời và thu nhỏ góc nhìn, có thể thấy màu trời do shader tạo ra ở nhiều góc khác nhau, từ mặt đất đến quỹ đạo
    • Hiệu ứng tương tự cũng được dùng cho ảnh poster nhá hàng bài viết đầu tháng 4, và ảnh render đã được chia sẻ qua tweet
    • Torus trong cảnh có thể vẫn trông như ở trạng thái “lit-up” ngay cả sau khi mặt trời lặn
    • Nguyên nhân là shadow-map hoặc shadow-camera của directional light chính có quy mô nhỏ, nên không bao phủ được torus ở quá xa
    • Một cách vòng tránh là tái sử dụng cách tiếp cận shadow-mapping từ bài viết về volumetric lighting, nhưng thực tế chưa được thử

Xử lý nhật thực

  • Khi thiên thể lớn che khuất mặt trời, có thể thêm xử lý bằng cách gọi hàm sunVisibility sau lightMarch, rồi nhân giá trị trả về [0, 1] vào độ truyền qua
  • Ý tưởng cơ bản là so sánh tích vô hướng giữa hướng mặt trănghướng mặt trời tại điểm mẫu hiện tại
  • Nếu hai hướng gần như trùng nhau và tích vô hướng gần 1.0, mặt trăng đang che mặt trời; nếu vuông góc và gần 0.0, sẽ không có che khuất
  • Chỉ dùng tích vô hướng đơn thuần sẽ không phản ánh được kích thước và tỷ lệ của object trong cảnh, nên phần cài đặt so sánh khoảng cách góc giữa mặt trời và mặt trăng cùng bán kính góc của từng bên
  • sunVisibility xử lý trường hợp mặt trăng không che mặt trời, trường hợp mặt trăng che khi nhìn từ camera thấy nó lớn hơn hoặc gần bằng mặt trời, và trường hợp mặt trăng che khi từ góc nhìn camera nó đi vào trong bán kính mặt trời
  • Demo bổ sung sunVisibilitymesh mặt trăng lên ví dụ tán xạ khí quyển hiện có, để khi căn mặt trăng thẳng hàng với mặt trời, Atmospheric Scattering shader sẽ xử lý tình huống thiếu sáng
  • Mô phỏng nhật thực và vành nhật hoa tinh vi hơn được trình bày trong bài báo Physically Based Real-Time Rendering of Eclipses, nhưng phần triển khai của bài báo này không được port sang WebGL

Khí quyển của các hành tinh khác

  • Mô hình mật độ khí quyển và tán xạ được dùng chủ yếu được quyết định bởi bán kính của hành tinh và khí quyển, cùng một số hằng số như RayleighScaleHeight, RayleighBeta, MieScaleHeight, MieBeta, mieBetaExt, mieG, OzoneHeight, OzoneWidth
  • Điều chỉnh các giá trị này có thể tạo ra kết quả gần với khí quyển Sao Hỏa hoặc khí quyển của các hành tinh khác
  • Các giá trị dùng cho Sao Hỏa là xấp xỉ
    • planetRadius: 3390
    • atmosphereRadius: 3500, dày khoảng 110 km
    • rayleighScaleHeight: 11.1
    • rayleighBeta: new THREE.Vector3(0.019, 0.013, 0.0057)
    • mieScaleHeight: 1.5
    • mieBeta: 0.04
    • mieBetaExt: 0.044
    • mieG: 0.65
    • ozoneCenterHeight: 0.0
    • ozoneWidth: 1.0
    • ozoneBetaAbs: new THREE.Vector3(0.0, 0.0, 0.0)
    • sunIntensity: 15.0
    • planetSurfaceColor: '#8B4513'
  • Thay các hằng số hiện có bằng những giá trị này sẽ cho ra bầu khí quyển bụi hơn và ngả cam hơn, đồng thời cũng tạo được sắc xanh đặc trưng lúc hoàng hôn của Sao Hỏa
  • Có thể tham khảo bài báo liên quan Physically Based Rendering of the Martian Atmosphere

Tán xạ khí quyển dựa trên LUT

  • Cách tiếp cận và những phần đã lược bớt

    • Shader trước đây có thể kết xuất khí quyển ở cả quy mô nhỏ lẫn lớn một cách trực quan, nhưng chi phí chạy cao do vòng lặp raymarching với nhiều PRIMARY_STEPS, vòng lặp lồng nhau lightmarching và việc tính toán ở độ phân giải toàn màn hình
    • A Scalable and Production Ready Sky and Atmosphere Rendering Technique của Sebastian Hillaire đề xuất phương pháp dựa trên Look Up Tables (LUTs), trong đó các phép tính tán xạ tốn kém được lưu vào texture, rồi ở bước render cuối sẽ lấy mẫu và tổng hợp các texture đã tính trước
    • Các LUT được đề cập gồm Transmittance LUT lưu lượng ánh sáng còn lại sau khi đi qua khí quyển, Sky-view LUT lưu màu bầu trời tại một vị trí camera cụ thể, và Aerial Perspective LUT lưu haze khí quyển cùng ánh sáng tán xạ giữa camera và hình học cảnh đang nhìn thấy
    • Tôi không chuyển nguyên xi toàn bộ phần triển khai trong bài báo; LUT phù hợp với compute shader của WebGPU, nhưng do thiếu thời gian và để giữ mạch bài viết nên vẫn dùng WebGL
    • Trong bài báo, Aerial Perspective LUT là 3D texture, nhưng trong phần triển khai này dùng render target 2D
    • Cách này đòi hỏi phải tạo lại texture để có giá trị pixel chính xác mỗi khi camera di chuyển, nên khó có thể tính sẵn trước
    • Multi-Scattering bị lược bỏ do thiếu thời gian
  • Transmittance LUT

    • Trong shader cũ, mọi điểm mẫu đều gọi lightmarch để tính xem ánh sáng mặt trời tới được bao nhiêu, và quá trình này rất tốn kém
    • Transmittance LUT lưu sẵn dữ liệu này ở độ phân giải thấp để các LUT khác có thể đọc lại khi cần dữ liệu ánh sáng
    • Phần triển khai định nghĩa một Frame Buffer Object riêng với độ phân giải 250 x 64, áp dụng material shader tùy chỉnh lên full-screen quad của scene chuyên dụng transmittanceLUTScene, rồi truyền texture kết quả render làm uniform cho các LUT downstream
    • Tại mỗi pixel, quá trình raymarching bắt đầu từ vec3(0.0, radius, 0.0), trong đó radius tăng từ planetRadius tới atmosphereRadius theo tọa độ vUv.y
    • Trục x của LUT biểu diễn góc ánh sáng, trục y biểu diễn độ cao; màu trắng thuần là độ truyền qua 100%, còn vùng đen hoặc có màu biểu thị mặt đất hoặc phần không khí dày nhất
    • Sau đó, các LUT khác có thể lấy được “lượng ánh sáng sống sót sau khi đi qua khí quyển ở một góc và độ cao cho trước” chỉ bằng một lần tra cứu texture
  • Sky-view LUT

    • Sky-view LUT tính màu bầu trời khi nhìn lên từ mặt đất theo một hướng cụ thể
    • getSkyViewRayDir ánh xạ vUv.x sang azimuth [-PI, PI], và vUv.y sang elevation [-PI/2, PI/2] để xác định hướng raymarching
    • Với elevation, nó dùng ánh xạ bậc hai (vUv.y * vUv.y - 0.5) * PI như một cách lách để tránh việc Sky View bị nhấp nháy quá nhiều ở khoảng cách xa
    • Nếu tia không đi vào khí quyển thì trả về màu đen; với tia chạm vào hành tinh, chỉ raymarch phần khí quyển nhìn thấy được và dừng sớm hơn khi chạm hành tinh
    • Vòng lặp tán xạ giống như trước, nhưng tiến dọc theo hướng Sky View và dùng Transmittance LUT cho ánh sáng mặt trời
  • Aerial Perspective LUT

    • Khác với bài báo của Hillaire, kết quả triển khai là texture 2D, và mỗi pixel tương ứng với đúng một pixel màn hình đang nhìn thấy
    • Nó dùng depth buffer của cảnh để xác định cần march xa bao nhiêu dọc theo tia đó và tích lũy tán xạ đến đâu
    • Gần như tái sử dụng nguyên mã tán xạ cũ, chỉ khác là mỗi mẫu sẽ lấy độ khả kiến của ánh sáng mặt trời từ Transmittance LUT
    • Đầu ra lưu tán xạ khí quyển tích lũy vào RGB, còn alpha lưu giá trị view transmittance đã được packed để dùng khi compositing
    • Luồng triển khai là đọc độ sâu từ depthBuffer, khôi phục vị trí không gian thế giới của pixel màn hình bằng getWorldPosition(vUv, depth), rồi tính rayDir từ vị trí camera tới vị trí thế giới đó
    • Sau đó dùng logDepthToRayDistance(vUv, depth) để chuyển độ sâu cảnh thành khoảng cách tia, tính giao cắt với khí quyển và hành tinh, rồi chỉ march phần khí quyển nhìn thấy được
  • Tổng hợp

    • Sau khi tạo Sky-view LUT và Aerial Perspective LUT, bước post-processing cuối sẽ kết hợp cả hai
    • Công việc cốt lõi là chuyển rayDir hiện tại sang tọa độ UV của Sky View
    • Với hình học cảnh, áp dụng Aerial Perspective LUT; kênh alpha được dùng làm view transmittance, còn kênh RGB là ánh sáng tán xạ, để tính color = color * aerialPerspective.a + aerialPerspective.rgb
    • Với pixel nền, lấy mẫu từ Sky View LUT; nếu depth >= 1.0 - 1e-7 thì xem là nền và áp dụng color = inputColor.rgb + sampleSkyViewLUT(rayDir, planetCenter)
    • Cuối cùng áp dụng ACESFilm(color)pow(color, vec3(1.0 / 2.2))
    • Có thể xem toàn bộ mã triển khai khí quyển dựa trên LUT tại Github link

Kết lại

  • Kết quả tán xạ khí quyển dựa trên LUT có thể trông gần như giống phiên bản raymarching hoàn chỉnh trước đó, nhưng quy trình bên trong thì khác
  • Công việc được chia thành các LUT nhỏ hơn rồi tổng hợp ở hiệu ứng cuối, thay vì ở mỗi mẫu lại raymarch lặp đi lặp lại về phía mặt trời để tính lượng ánh sáng đến được
  • Nhờ lấy trực tiếp thông tin chiếu sáng từ Transmittance LUT, nó thay các vòng lặp lồng nhau tốn kém bằng các lần tra cứu texture đơn giản và mang lại cải thiện hiệu năng đáng kể ở cảnh cuối
  • Phần triển khai này vẫn còn kém hơn so với Sébastian Hillaire và các triển khai trong lĩnh vực khác; đặc biệt Sky View còn bị banding và flickering, và do các phần bị lược bớt nên cũng chưa thật sự tối ưu
  • Có lẽ ngay từ đầu nên dùng WebGPU
  • Nếu cần một triển khai production-grade thực sự, tôi khuyên dùng three-geospatial của Shoda Matsuda(@shotamatsuda)
  • Tôi cũng đã làm thêm phần chồng volumetric clouds lên trên, nhưng kết quả hiện vẫn còn lẫn lộn và chưa đủ hài lòng để đưa vào bài viết, nên cần làm thêm nữa

1 bình luận

 
Ý kiến trên Hacker News
  • Tôi đã xem cái này từ trước nên có thể không hoàn toàn liên quan, nhưng video của Sebastian Lague nói về kết xuất khí quyển trong một thử nghiệm tạo hành tinh cũng rất thú vị https://www.youtube.com/watch?v=DxfEbulyFcY
    Có một kiểu thú vị rất riêng khi phát triển hiệu ứng hình ảnh rồi nhìn nó dần dần trở nên giống thực tế hơn, và tôi cũng muốn tự mình thử nghiệm lĩnh vực này vào một ngày nào đó
    • Điều đáng kinh ngạc nhất về Sebastian Lague là thuật toán YouTube có thể phá hỏng con người đến mức nào
      Trước đây video của anh ấy đạt hàng triệu lượt xem, giờ thì ngay cả 500 nghìn cũng chỉ vừa vượt qua. Cũng có thể là do thời kỳ COVID mọi người đều ở nhà và chú ý đến những thứ ngẫu nhiên hơn
    • Lời phàn nàn duy nhất của tôi về Sebastian Lague là video không đủ nhiều
      Tôi thường bật lên khi đi ngủ, và vì tôi rất muốn có thêm kiểu nội dung đào sâu các chủ đề kỹ thuật theo cách điềm tĩnh nhưng sâu sắc như vậy nên đã từng nghĩ đến chuyện tự làm
  • Không rõ có phải cố ý bỏ qua hay không, nhưng có một điểm đáng nói là trong mô hình hoàng hôn, bầu trời không nên chuyển sang màu đen ngay khi mặt trời vừa xuống dưới đường chân trời
    Sau khi mặt trời lặn, trong một khoảng thời gian, tầng khí quyển phía trên đầu và vùng phía trên đường chân trời vẫn còn nhận ánh sáng mặt trời, và trong khí quyển Trái Đất, ánh chạng vạng dễ nhận thấy vẫn còn cho đến khi mặt trời xuống tới 18 độ dưới đường chân trời. Có thể dùng dò tia thì không thực tế lắm, nhưng có những thuật toán phổ biến để mô hình hóa điều này
  • Những bài viết đồ họa hay luôn được chào đón. Tôi cũng đã làm thứ tương tự trong một trình tạo vũ trụ/hành tinh theo thủ tục, và điểm hay của tán xạ khí quyển là khi kết hợp với kết xuất mây thể tích, nó có thể tạo ra những cảnh hoàng hôn và bầu trời rất đẹp
    https://www.threads.com/@mrsharpoblunto/post/DVS4wfYiG8f?xmt...
    https://www.threads.com/@mrsharpoblunto/post/C6Vc-S1O9mX?xmt...
    https://www.threads.com/@mrsharpoblunto/post/C6apksDRa8q?xmt...
  • Thật đáng kinh ngạc là điện thoại và trình duyệt ngày nay có thể làm được những gì
    Tôi còn nhớ mình đã từng triển khai bài “Display of The Earth Taking into Account Atmospheric Scattering” của Nishita và cộng sự, một bài báo từ năm 1993 gần như là tiền thân của chủ đề này và rất dễ đọc: https://www.researchgate.net/publication/2933032_Display_of_...
    • Tôi chợt nhớ ra bài báo mình đã đọc khi từng triển khai tán xạ Rayleightán xạ Mie trong một bình luận khác, và đúng là bài này
      Khi làm cho nó chạy được, tôi đã có khoảnh khắc nhận ra rằng “à, hóa ra hiện tượng thế giới thực phức tạp này có thể được mô hình hóa khá tốt chỉ bằng vài phép tính tương đối đơn giản”. Từ một skybox xanh tĩnh, nó lập tức chuyển thành một chu kỳ ngày đêm hoàn chỉnh
  • Thật sự xuất sắc
    Trước đây tôi từng nghĩ thử kết xuất bầu trời trên web bằng cách chồng nhiều gradient lên nhau xem sao. Có lẽ tôi đã thành công ở mức nào đó và cho ra kết quả tạm ổn, nhưng hoàn toàn không thể so với thứ được làm ở đây. Thành phẩm rất ấn tượng và truyền cảm hứng
    • Trước đây tôi từng triển khai tán xạ Rayleightán xạ Mie trong một game engine làm như sở thích
      Chỉ riêng điều đó thôi cũng đã cho ra chu kỳ hoàng hôn/bình minh khá thuyết phục, và nếu tôi nhớ không nhầm thì ngay cả mặt trời cũng như tự nhiên xuất hiện từ đó. Tôi đã dùng XNA, nền tảng phát triển game C# của Microsoft, và làm theo loạt hướng dẫn tuyệt vời của Riemer; bản lưu trữ ở đây https://github.com/SimonDarksideJ/XNAGameStudio/wiki/Riemers...
      Tuy nhiên tôi không thấy phần về tán xạ ở đó, nên có thể tôi đã lấy phần đó từ chỗ khác. Tôi vẫn nhớ đã đọc các bài báo có công thức
  • SpaceEngine cũng nổi tiếng là đầu tư rất nhiều vào lĩnh vực này, cực kỳ đáng thử: https://www.youtube.com/watch?v=_4TjdVAbXks
    https://spaceengine.org/
    • Tôi thích phần FAQ của những thứ như thế này vì nó cho thấy quy mô và sự đa dạng của các câu hỏi
      Câu trả lời cho “SpaceEngine có bao nhiêu vật thể?” là toàn bộ danh mục sao Hipparcos, toàn bộ ngoại hành tinh đã biết, hơn mười nghìn thiên hà, phần lớn các vật thể trong Hệ Mặt Trời, tổng cộng 130 nghìn, cộng thêm còn nhiều thiên hà và hệ sao hơn cả số thực sự tồn tại trong toàn bộ vũ trụ quan sát được. Với “Làm sao một hành tinh nước có thể nóng được?”, câu trả lời là nước ở tầng khí quyển trên là hơi nước nóng, nhưng khi xuống thấp hơn thì chuyển mượt sang dạng lỏng dưới áp suất cao, và sâu hơn nữa thì thành trạng thái rắn gọi là ice VII. Còn câu trả lời cho “Di chuyển thế nào?” là các phím WASD
    • Một trong những trải nghiệm game nửa mang tính giáo dục mà tôi thích nhất là mở Wikipedia ở một tab và SpaceEngine ở tab còn lại
      Đây là một game tuyệt vời, và dù đã khá cũ, tôi vẫn chưa thấy thứ gì tốt đến vậy
    • Đây là phần mềm tuyệt vời đã có từ nhiều năm nay, và không chỉ ở chủ đề này mà ở rất nhiều mặt khác, sự ám ảnh với chi tiết của nó thật đáng nể
      Đọc bài này làm tôi cũng nghĩ ngay đến SpaceEngine
  • Tán xạ từ lâu đã là chìa khóa để tạo ra hình ảnh kết xuất chân thực
    Một trong những bài báo tôi thích: http://www.graphics.stanford.edu/papers/bssrdf/bssrdf.pdf
    Có lẽ đây là lần đầu tôi biết rằng kết xuất sữa là một bài toán khó nhằn
  • Wow, đúng là một hành trình khá kinh khủng
    Có lẽ tôi chỉ hiểu được khoảng 5%, nhưng vẫn cực kỳ thán phục
    • Tôi cũng vậy. Chỉ riêng tư liệu hình ảnh thôi cũng đã đáng để đọc rồi
  • Ồ, đây thật sự là một bài viết đẹp và dễ đọc
    Hơn nữa, nếu là giấy phép MIT thì coi như vấn đề skybox trong game của tôi đã được giải quyết. Vì phối cảnh sẽ cố định, tôi chỉ cần phần kết xuất mặt trời di chuyển qua bầu trời, rồi có thể mở rộng bằng chu kỳ sóng sin cho sự thay đổi góc mặt trời theo mùa trong năm