1 điểm bởi GN⁺ 2024-06-06 | 1 bình luận | Chia sẻ qua WhatsApp
  • Mặt trăng và hành tinh khí dùng làm nền trời có thể biểu diễn bề mặt, chuyển động quay và khí quyển chỉ bằng một đĩa tròn và pixel shader, không cần mesh hình cầu thật
  • UV sphere truyền thống dễ ánh xạ texture 2×1, nhưng kéo theo đường viền góc cạnh, mất mẫu mipmap, mẫu lặp, seam, méo ở cực và giới hạn của halo khí quyển
  • Cách dùng đĩa khôi phục vị trí bề mặt cầu từ UV tâm, rồi dùng UV cầu dựa trên asin, ma trận quay và spin bằng cách dịch tọa độ u để tạo bề mặt trông như hình cầu ở cấp pixel
  • Phần shading bắt đầu từ chiếu sáng Lambertian dùng vị trí bề mặt làm normal; với bề mặt đá áp dụng ma trận TBN và normal map, còn khí quyển thì xấp xỉ độ sáng halo và hấp thụ theo từng kênh
  • Có thể xử lý texture tĩnh từ Substance Designer và texture render động trong Unreal theo cùng một cách, giảm gánh nặng hiệu năng bằng texture động nhỏ hơn và điều chỉnh tiling

Mục tiêu thị giác của hành tinh trong skybox

  • Skybox của dự án tập trung tạo bố cục giống bầu trời ngoài hành tinh với mặt trăng có hoạt ảnhhành tinh khí
  • Hai hành tinh quay, và hành tinh khí còn có dòng khí quyển chuyển động
    • Chuyển động thực tế khá tinh tế, nhưng được tăng tốc để tạo hiệu ứng thị giác
  • Hướng mỹ thuật là phong cách bán hiện thực (semi-realistic) lấy cảm hứng từ tranh SF thập niên 1970~80

Đầu vào texture bề mặt và ràng buộc

  • Bề mặt hành tinh khí được tạo thời gian thực bằng pixel shader và cơ chế render-to-texture
  • Texture mặt trăng được tạo trong Substance Designer, có thể thay đổi bảng màu và material seed để thử nhiều look khác nhau
  • Hành tinh khí động phải render texture mỗi frame nên chi phí hiệu năng lớn
    • Tăng độ phân giải texture lên 2 lần thì chi phí render tăng 4 lần
  • Cần một giải pháp hoạt động nhất quán cho cả texture tĩnh từ Substance Designer lẫn texture render động trong Unreal
  • Cubemap ban đầu đã được cân nhắc, nhưng có thể dùng tài nguyên gấp 6 lần so với texture vuông, và Substance Designer không hỗ trợ tạo cubemap nên không thực tế

Giới hạn bộc lộ ở UV sphere

  • UV sphere trông như lựa chọn mặc định vì dễ khớp tọa độ với texture chữ nhật 2×1
  • Vì mỗi lần chỉ thấy một phía của hình cầu, cũng có thể dùng texture vuông dạng tile thay vì một texture chữ nhật độc nhất cho toàn bộ bề mặt
  • Khi áp dụng thực tế, nhiều vấn đề chồng lên nhau
    • Đường viền trông góc cạnh rõ rệt, cần tăng subdivision hoặc che bằng mask trong pixel shader
    • Phần lớn bề mặt cầu được nhìn xiên nên bị lấy mẫu từ mipmap thấp, và với texture động, nhiều pixel tạo ra mỗi frame được dùng cho vùng gần như không thấy được
    • Tiling phải tăng theo bội số nguyên thì toàn bộ pattern mới nối liền, và chỉ cần tăng 2 lần là đã thấy lặp
    • Fractional tiling tạo ra seam chạy từ cực Bắc đến cực Nam
    • Ở các cực xuất hiện texture pinching và méo ánh xạ tam giác, trông tương tự méo texture rung lắc của PSX
    • Chỉ có thể vẽ bề mặt, nên atmospheric halo cần một model riêng
  • UV sphere vẫn dùng được cho mô hình hóa hành tinh, nhưng với mục đích skybox thì phải sửa và hack nhiều

Cách tiếp cận bằng đĩa tròn và pixel shader

  • Hành tinh trong skybox được quan sát từ một vị trí ở xa, nên không nhất thiết cần mesh cầu 3D phức tạp
  • Dùng một đĩa polygon tròn được tô kín đơn giản, rồi xử lý ánh xạ texture trong pixel shader
  • Có thể vẽ cả atmospheric halo trong cùng một mesh
  • Gốc UV của đĩa phải nằm chính xác ở tâm
    • Nếu xem bán kính hành tinh là 1, tọa độ UV phải mở rộng ra ngoài mức đó để có không gian vẽ khí quyển

Khôi phục bề mặt cầu

  • Hệ tọa độ sử dụng hệ left-handed, Y-up nhất quán với DirectX
    • Unreal Engine cũng là left-handed nhưng hướng up là Z, nên cần kiểm tra hướng và định dạng normal map
  • Phương trình bề mặt ánh xạ vị trí 2D trên mặt phẳng đĩa sang vị trí 3D trên bề mặt cầu
  • Trước tiên, để xác định pixel có nằm trong bề mặt cầu hay không, kiểm tra độ dài vector UV có nhỏ hơn bán kính hay không
float CircleMask( float2 uv, float radius)
{
    return length(uv) < radius? 1.0: 0.0;
}
  • Cách này chỉ hoạt động đúng khi tọa độ UV được căn thẳng với tâm mesh
  • local position của bề mặt cầu dùng nguyên x, y của UV và chỉ khôi phục z
float3 ReconstructSurface(float2 uv)
{
    float zSquared = 1.0 - dot(uv, uv);
    float z = sqrt(zSquared);
    return float3(uv, z);
}

Tạo UV cầu

  • Quá trình ánh xạ texture vuông lên bề mặt cầu được chia thành ba bước
    • Quấn quanh hình trụ
    • Lặp lại xử lý tương tự trên trục y
    • Biến đổi kết quả thành dạng tròn
  • Tọa độ texture x tỉ lệ với góc quấn quanh hình trụ; tính arcsine(x) rồi ánh xạ lại [-π, π] sang [0, 1]
  • Dùng generatrix, tức bề rộng local của hình cầu, để pinching tọa độ ở các cực
float2 GenerateSphericalUV(float3 position)
{
    float width = sqrt(1.0 - position.y * position.y);
    float generatrixX = position.x / width * sign(position.z);
    float2 generatrix = float2(generatrixX, position.y);
    float2 uv = asin(generatrix) / 3.14159 + float2(0.5, 0.5);
    return float2(uv);
}

Độ nghiêng trục và xoay

  • Để bố cục hành tinh tự nhiên hơn, nghiêng trục hành tinh bằng pitch và roll
    • Hành tinh trong tranh SF thường bị nghiêng, giúp bố cục trông năng động và có thể cho thấy ice cap vùng cực
  • Yaw tương ứng với chuyển động quay, và được xử lý ở bước riêng để tránh vấn đề seam
  • Với mesh cầu, thường biến đổi hành tinh bằng ma trận; nhưng ở cách dùng đĩa, mỗi pixel định nghĩa vị trí bề mặt nên có thể dùng một ma trận 3×3 đơn giản chỉ chứa phép quay
float3x3 CreateRotationMatrix(float pitch, float roll) {
    float cosPitch = cos(pitch);
    float sinPitch = sin(pitch);
    float cosRoll = cos(roll);
    float sinRoll = sin(roll);

    return (float3x3)(
        cosRoll, -sinRoll * cosPitch, sinRoll * sinPitch,
        sinRoll, cosRoll * cosPitch, -cosRoll * sinPitch,
        0.0, sinPitch, cosPitch
    );
}
  • Material editor của Unreal không hỗ trợ ma trận như một kiểu dữ liệu, nhưng có cách lách

Xử lý scale, seam, spin

  • Khi thay đổi scale của texture, UV seam vốn được giấu ở rìa texture ban đầu sẽ lộ ra
  • Seam không thể loại bỏ hoàn toàn một cách dễ dàng, nhưng nếu chuyển nó ra phía sau thì sẽ ít thấy hơn
  • Chia hình cầu thành các góc phần tư theo sign của position, rồi áp dụng offset theo hướng u cho các góc phần tư back-left và back-right
float2 GenerateSphericalUV(float3 position, float spin, float scale)
{
    float leftRightSign = sign(position.x);
    float frontBackSign = sign(position.z);
    float width = sqrt(1.0 - position.y * position.y);
    float generatrixX = position.x / width * frontBackSign;
    float2 generatrix = float2 (generatrixX, position.y);
    float2 uv = asin(generatrix) / 3.14159 + float2 (spin, 0.5);
    if(frontBackSign < 0.0)
    {
        uv = float2 (uv.x + 1.0 * leftRightSign, uv.y);
    }
    return float2 (uv / scale);
}
  • Seam vẫn nối cực Bắc và cực Nam, nhưng đã được chuyển ra phía sau nên ít lộ hơn nhiều
  • Chuyển động quay được xử lý bằng cách dịch texture bề mặt theo hướng tọa độ u, chứ không xoay chính hình cầu
float2 GenerateSphericalUV(float3 position, float scale, float spin)
{
    float width = sqrt(1.0 - position.y * position.y);
    float generatrixX = position.x / width * sign(position.z);
    float2 generatrix = float2(generatrixX, position.y);
    float2 uv = asin(generatrix) / 3.14159 * scale + float2(0.5 + spin, 0.5);
    return float2(uv);
}
float2 sphericalVU = GenerateSphericalUV(position, scale, time*speed)

Shading bề mặt và normal map

  • Để hành tinh trông tròn, shading rất quan trọng
  • Khác với shading trong engine thông thường, hành tinh có hai layer là bề mặt và khí quyển, nên phải tự định nghĩa ánh sáng và blending cho hai layer
  • Khi bán kính là 1, vị trí bề mặt trước khi quay có thể dùng ngay làm surface normal
  • Ánh sáng dùng mô hình Lambertian đơn giản
    • Mục tiêu không phải hiện thực tuyệt đối, mà là thẩm mỹ bìa SF cách điệu lấy cảm hứng từ ảnh NASA
float LambertianLight(float3 normal, float3 lightDirection) {
    float NdotL = max(dot(normal, lightDirection), 0.0);
    return NdotL;
}
  • Bề mặt mịn như gas giant có thể chỉ cần normal đơn giản là đủ
  • Với hành tinh đá, nên mô phỏng địa hình như núi, sống núi, hố va chạm bằng normal map
  • Để dùng normal map cần ma trận TBN gồm Tangent, Bitangent, Normal
    • Với hình cầu, phép tính này phải thực hiện theo từng pixel
    • Ma trận TBN được dùng để chuyển normal từ normal map sang World Space Coordinates

Bất liên tục UV và artifact mipmap

  • UV tạo trong pixel shader có thể gặp vấn đề bất liên tục (discontinuity)
  • Back seam cần thành phần UV ngang nối hoàn toàn từ 0.0 sang 1.0 ở biên, nhưng thực tế sẽ xuất hiện artifact dạng block dọc theo seam
  • Đường này tương ứng với DDX của UV
    • DDX và DDY đo tốc độ thay đổi UV theo các trục trong screen space
    • Texture sampler dùng các giá trị này để quyết định mipmap sẽ dùng
    • UV derivative thấp tương ứng với mipmap độ phân giải cao, còn derivative cao tương ứng với mipmap độ phân giải thấp
  • Khi giá trị hai bên seam nhảy đột ngột, DDX tăng lớn và texture bị lấy mẫu từ mipmap thấp nhất, có thể trông như một đường xám
  • Trên hành tinh, vùng này sẽ được che bằng polar patch nên không quá nghiêm trọng; nhưng nếu thấy rõ, cần tự hiệu chỉnh DDX và DDY của seam rồi truyền cho sampler
  • Ví dụ fix chỉ nhằm minh họa; giá trị thực tế phải tự suy ra công thức cho phù hợp với mapping

Polar patch và hiệu chỉnh pinching

  • Méo ở cực được che bằng polar patch theo cách đã được kiểm chứng
  • Trên hành tinh hoặc mặt trăng, vùng cực thường phủ băng và khác biệt thị giác so với các vùng khác, nên một patch texture riêng vừa che được vấn đề vừa tăng điểm nhấn thị giác
  • Có thể thêm polar patch bằng cách ánh xạ lên một mặt phẳng vuông góc với trục quay rồi xoay tọa độ kết quả
float2 PolarPatchMapping(float3 position, float scale, float spin)
{
    float cosSpin = cos(spin);
    float sinSpin = sin(spin);
    float scale = 0.4;

    float2 uv = float2(position.x, position.z) * scale;
    float2 spinningUV = float2(uv.x * cosSpin - uv.y * sinSpin,
                               uv.x * sinSpin + uv.y * cosSpin);

    return spinningUV;
}
  • Vì UV map không được lưu ở vertex mà được tạo cho từng pixel, việc hiệu chỉnh pinching ở cực linh hoạt hơn
  • Có thể hiệu chỉnh aspect ratio của texture vùng cực bằng một phép chia đơn giản
uv = float2(uv.x, uv.y/pow(width, 1/3));
  • Kết quả hiệu chỉnh có thể trông như một artifact khác, nhưng trong cảnh thực tế, nó nối với polar patch mượt hơn và giảm bất nhất thị giác

Xấp xỉ halo khí quyển

  • Vì bề mặt hành tinh không chiếm toàn bộ vùng đĩa, có thể vẽ atmospheric halo ở phần không gian còn lại
  • Nếu vẽ halo trong cùng shader, nó có thể hòa tự nhiên với khí quyển trên hành tinh
  • Render khí quyển dựa trên vật lý thường dùng Rayleigh scattering và raymarching, nhưng ở đây không dùng
  • Sử dụng một xấp xỉ tương tự trick chiếu sáng đối tượng động trong Quake 1·2
    • Static lighting của Quake được tính trước bằng lightmap
    • Quái vật động lấy mẫu lightmap bên dưới để điều chỉnh màu; dù không chính xác về vật lý nhưng giúp chúng hòa với môi trường
  • Theo cách tương tự, dùng bề mặt hành tinh để xấp xỉ ánh sáng của atmospheric halo
    • Mở rộng surface normal trước bump mapping để tính độ sáng halo
    • Remap độ sáng theo từng kênh khác nhau có thể mô phỏng đơn giản sự hấp thụ các bước sóng ánh sáng khác nhau
    • Khí quyển dễ thấy hơn ở góc xiên, nên remap thành phần Z của surface normal
    • Cũng tính khoảng cách từ bề mặt cầu để tạo hiệu ứng halo fade
  • Cuối cùng, gộp bề mặt và khí quyển bằng alpha blending

Kết quả cuối cùng và ứng dụng

  • Tuy cần nhiều bước hơn dự kiến, kết quả cuối cùng trông như một hình cầu hoàn chỉnh và hỗ trợ các thao tác texture·shader mong muốn
  • Có thể tạo texture bề mặt bằng một graph Substance Designer duy nhất, và kết quả không cần hậu xử lý bổ sung
  • Texture có hoạt ảnh cũng có thể xử lý theo cùng cách
    • Có thể dùng kích thước texture nhỏ hơn để tiết kiệm hiệu năng
    • Có thể điều chỉnh tiling theo mức cần thiết
  • Có thể xem video kết quả tại Video 3

1 bình luận

 
GN⁺ 2024-06-06
Các ý kiến trên Hacker News
  • Có vẻ tác giả đã loại trừ cubemap quá nhanh; khi dùng nó để render một hành tinh khí động trong dự án cá nhân, tôi thấy đây là giải pháp đơn giản nhất.
    Dùng cubemap không làm bộ nhớ tăng gấp 6 lần; về bản chất là chia một texture hình chữ nhật lớn thành 6 mặt chữ nhật nhỏ, nên tổng mức chi tiết của texture vẫn như nhau.
    Ưu điểm là không phải bận tâm đến hiện tượng nén ở hai cực, và có thể dễ dàng tạo trường dòng chảy liền mạch để hoạt ảnh/biến dạng texture bằng hàm nhiễu 3D hoặc 4D.
    https://www.junkship.net/News/2016/06/09/jupiter-jazz

    • Để tránh các artifact đường nối kỳ lạ, dùng 2 bán cầu chiếu lập thể có thể tốt hơn.
      Mỗi bán cầu được chiếu thành một đĩa tròn, nhưng có thể lấp texture đến tận biên hình vuông và chồng lặp một phần của mỗi bán cầu lên các góc của texture bên kia.
      Vì có chênh lệch tỉ lệ 1:2 giữa tâm và rìa đĩa, có thể xem là lãng phí pixel nếu tính theo mức chi tiết tối thiểu; nhưng vì đây là phép chiếu đồng góc, nó khiến cách lấy mẫu màu pixel đích ở các góc nhìn xiên gắt bớt khó hơn nhiều, và tính xuôi/ngược cũng chỉ cần 1 phép chia cùng vài phép cộng, nhân cho mỗi điểm chiếu, rẻ hơn cả phép chiếu xuyên tâm của cubemap.
      Nếu cần một phương pháp đồng góc không quá khó về mặt khái niệm nhưng giảm thêm biến thiên tỉ lệ và lãng phí pixel ở góc, có thể dùng 2 phép chiếu Mercator hơi chồng lên nhau theo các hướng vuông góc để phủ hình cầu như hai mảnh da của quả bóng chày.
      Mỗi mảnh có thể là một texture hình chữ nhật, và trong các bài báo của NOAA cũng có bài đề xuất cách tiếp cận này cho lưới phương trình vi phân trong mô phỏng thời tiết Trái Đất.
      Phép chiếu tiết kiệm pixel nhất mà tôi biết là chia hình cầu thành một bát diện, rồi phủ từng octant bằng lưới pixel lục giác dựa trên “tọa độ diện tích cầu”.
      Mỗi octant có thể được biểu diễn trong ảnh pixel vuông thông thường như nửa hình vuông, tức tam giác vuông 45-45-90, cho ra kết quả như https://observablehq.com/@jrus/sac-quincuncial, và cũng có thể dùng lưới lục giác như https://observablehq.com/@jrus/sphere-resample.
      Tuy nhiên, xử lý chi tiết khi phải lấy mẫu vượt qua đường biên rắc rối hơn nhiều so với cách dùng 2 phép chiếu lập thể, và vẫn có thể sinh artifact ở đường nối.
    • https://en.wikipedia.org/wiki/Peirce_quincuncial_projection
    • Nếu muốn giảm méo hơn nữa, cũng có thể gom các mặt tam giác của khối hai mươi mặt đều thành 10 hình thoi, rồi phủ mỗi hình thoi bằng một texture vuông.
      Cần nhiều toán hơn, nhưng nếu không định chia nhỏ bề mặt thêm vì lý do khác thì thường không đáng.
    • Có lẽ tôi có thể thử dùng curl noise đó để tạo mây trong trò chơi quy mô hành tinh mà tôi đang làm.
  • Hiện tượng nén texture ở cực thực ra là dạng biểu hiện cực đoan của méo tồn tại trên toàn bộ bề mặt.
    Thường nó chỉ rõ ở các cực, nhưng nếu chia nhỏ thích nghi hình cầu thành các tam giác, mức méo cũng thay đổi khi cấp độ chia nhỏ thay đổi, nên có thể nhìn thấy ở những nơi khác.
    Vấn đề là hình cầu được chia thành các tứ giác, và mỗi tứ giác trong không gian UV được biểu diễn bằng 2 tam giác có cùng diện tích, nhưng trong không gian 3D thì một tam giác—đặc biệt là tam giác có cạnh ngang gần cực hơn—lại nhỏ hơn.
    Dù vậy, UV vẫn được nội suy tuyến tính bên trong tam giác, nên một nửa texture bị thu nhỏ còn nửa kia bị kéo giãn.
    Ở cực, diện tích 3D của một tam giác thực sự trở thành 0, khiến chỉ một nửa texture được render và đường nối giữa các tam giác trở nên rõ rệt.
    Cách giải đúng là tính tọa độ UV cho từng pixel trong pixel shader thay vì nội suy tuyến tính theo đỉnh; nếu làm đúng, ngay cả cực cũng được xử lý liền mạch.

    • Tôi mới bắt đầu động chạm mỗi thứ một chút về render, shader và phát triển game; nếu có gợi ý, từ khóa hoặc liên kết để tìm hiểu thêm thì tôi rất muốn biết.
    • Nếu không đặt W trong mục XYZ là 1 mà scale W thì không sửa được vấn đề này sao?
    • Có trường hợp nào render hình cầu với nhiều cực render để giảm kiểu nén này không?
      Ví dụ khi render một hình cầu có cực “chính bắc”, nếu nhìn từ bên cạnh thì dùng cực render phía chính bắc, còn nếu nhìn từ gần chính bắc thì dùng render theo xích đạo ở 0',0’.
  • Tôi thấy muốn xem lại displacement mapping.
    Có lẽ nó không thay thế được vấn đề tác giả đang cố giải, nhưng đơn giản hơn và khá thú vị.
    Khoảng 25 năm trước, tôi đã làm một visualizer âm nhạc tên là “Eclipse” cho SoundJam; đầu vào là mảng mức tín hiệu trong dải tần nghe được và hai kênh trái/phải.
    Mục tiêu là tạo hình ảnh giống một mặt trời bị nhật thực với các luồng phát xạ corona, và dữ liệu nhạc trở thành các “luồng phát xạ” đó.
    Tần số của dữ liệu quyết định nó xuất hiện ở đâu quanh vành đĩa mặt trời.
    Theo thời gian, các luồng phát xạ đi xa khỏi mặt trời và “nguội đi” rồi biến mất thành màu đen; tín hiệu mạnh bắt đầu bằng màu trắng, rồi khi yếu dần chuyển sang vàng, cam, đỏ và nâu.
    Tôi phải giữ một mảng giá trị dữ liệu âm thanh trong một bộ đệm vòng đủ lớn để chứa khoảng thời gian trước khi luồng phát xạ biến mất thành màu đen.
    Toàn bộ phần hiển thị “Eclipse” và các luồng phát xạ cuối cùng là một displacement map, và tôi đã tính sẵn một bitmap trong đó giá trị của từng pixel là offset vào bộ đệm mức âm thanh.
    Pixel gần bề mặt mặt trời có offset tới dữ liệu mới đi vào, còn pixel ở phía ngoài có offset tới phần đuôi bộ đệm sắp hết hạn.
    Vì phải ánh xạ phát xạ tròn/xuyên tâm sang tọa độ Descartes, việc tạo giá trị dịch chuyển cần một chút toán.
    Vòng lặp chính nhận giá trị âm thanh mới, ghi đè giá trị cũ nhất, rồi duyệt displacement map theo hàng và cột để lấy dữ liệu âm thanh tương ứng, ánh xạ nó sang màu trong bảng màu cố định và đưa vào bộ đệm hiển thị.
    Nó không hào nhoáng như các visualizer khác, nhưng có vẻ đẹp trầm tĩnh và phản ánh dữ liệu nhạc khá tốt.
    Khác với các visualizer về sau, nó không có cảm giác như vẫn không thể đứng yên ngay cả khi đưa vào khoảng lặng.

  • Bài viết rất hay, nhưng càng cuộn xuống dưới càng tải nhiều shader, nên nếu không phải máy tính rất mạnh thì trình duyệt có thể gần như treo.

    • “Máy tính rất mạnh” là cỡ nào? Ngay cả đồ họa tích hợp trên laptop trong vòng 10 năm trở lại đây cũng khó render những thứ này sao?
  • Thật thú vị khi thấy mọi người diễn giải “tính chân thực” khác nhau đến mức nào
    Trong các game thời tài nguyên còn hạn chế hơn nhiều, sự khác biệt này đặc biệt rõ; có game chỉ với đồ họa vuông vức, pixelated và bảng màu thấp mà vẫn tạo cảm giác nhập vai hơn cả các game kinh phí lớn ngày nay

    the gas giant’s surface texture will be generated in realtime using a pixel
    shader and a render-to-texture approach
    Thật thú vị khi thấy tác giả ngay từ đầu đi theo một cách tiếp cận phi thực tế, không có ranh giới, tìm ra cách tạo nút thắt hiệu năng rồi cứ tiếp tục đi sâu xuống hang thỏ
    Cuối cùng không biết tác giả bài gốc có làm cho nó chạy được không
    Có lẽ đây cũng là khác biệt giữa dự án có ràng buộc thực tế và dự án sở thích
    Có thể dùng texture animation render sẵn cho hành tinh khí, chuyển sang phần còn lại của dự án, rồi khi toàn bộ đã chạy thì quay lại với màn cosplay Slartibartfast cũng được

    • Có lẽ các game độ trung thực thấp tạo cảm giác nhập vai hơn vì chúng chừa chỗ cho trí tưởng tượng tham gia
      Vì vậy có lẽ người chơi đã nhập tâm chủ động hơn
  • Bài viết này về một cách tiếp cận tương tự làm tôi nhớ đến: https://bgolus.medium.com/rendering-a-sphere-on-a-quad-13c92...

  • GPU hay thư viện 3D không có hàm nào quét ngang từng dòng của hình tròn rồi ánh xạ X,Y của texture vào vị trí “3D” trên hình cầu sao?
    Hình cầu là hình tròn, thuật toán vẽ hình tròn thì đơn giản, còn việc xoay và chiếu cả triệu đỉnh cho tam giác trông như một sự lãng phí tài nguyên khổng lồ
    Chỉ cần quét ngang, xuống một dòng rồi tiếp tục vẽ là có thể vẽ hết mọi dòng
    Tôi nhớ cuối thập niên 80 từng làm việc kiểu này để tính trước những thứ như hiệu ứng thấu kính phóng đại trong Second Reality

    • Có lẽ vì nội suy tuyến tính cầu chậm hơn nội suy tuyến tính và là xử lý chuyên biệt hơn
      Toàn bộ pipeline render vốn đã được thiết kế để xử lý nội suy tuyến tính
      Chỉ với một chương trình shader và 1 tam giác cũng có thể vẽ được một hình cầu khử răng cưa hoàn hảo
    • GPU không rasterize theo từng dòng như các software renderer ngày xưa; về cơ bản nó chỉ biết raster hóa tam giác
      Nếu render trên GPU thì bất chấp độ phức tạp bề ngoài, cách tiếp cận trong bài trông khá ổn
      Có một loạt bài rất hay về kiến trúc render dựa trên GPU: https://fgiesen.wordpress.com/2011/07/09/a-trip-through-the-...
      Phần 6 nói về rasterization
    • Với đoạn mã GPT đưa ra, cách làm là tính z tại mỗi pixel bên trong hình tròn, xoay nó, rồi dùng atan2 và asin để lấy tọa độ texture rồi tô màu; chạy thử thì có vẻ hoạt động
  • Hành tinh imposter bằng pixel shader 2D trên nền thật sự giúp ích rất nhiều cho các tác vụ có nhiều hành tinh, như vũ trụ procedural
    Do bộ nhớ và băng thông GPU, hành tinh dựa trên spherical cube chỉ giới hạn ở 1 instance, còn các thiên thể ở gần được vẽ bằng kỹ thuật nền
    Trong không gian có một khoảng cách nằm đâu đó giữa hai thiên thể mà cả hai đều được render dưới dạng imposter; sau đó thiên thể gần nhất được xử lý bằng phương pháp quadtree cầu
    Không hoàn hảo, nhưng ảo giác này gần như khó bắt bẻ
    Hơn nữa vì nằm trên mặt phẳng, việc thêm các hiệu ứng vật lý nhẹ của ống kính camera cho các thiên hà hay thiên thể ở xa cũng dễ hơn
    Ví dụ như hiệu ứng mặt trăng khi mọc trông lớn hơn thực tế, hoặc thiên hà xa trông bị bẻ cong bởi hấp dẫn

  • Tôi thích trang liên quan đến hành tinh khí này: https://emildziewanowski.com/flowfields/

  • Icosphere có thể được trải phẳng mượt hơn nhiều, vị trí đỉnh và kích thước phần tử cũng đều đặn
    Việc trải phẳng không hẳn tầm thường, nhưng vẫn làm được