使用著色器在WebGL3D場景中呈現行星錶面地形

来源:https://www.cnblogs.com/ljzc002/archive/2018/06/06/9134272.html
-Advertisement-
Play Games

實驗目的:按照一定規律生成類地行星地表地形區塊,並用合理的方式將地形塊顯示出來 涉及知識:Babylon.js引擎應用、著色器編程、正態分佈、數據處理、canvas像素操作 github地址:https://github.com/ljzc002/ljzc002.github.io/tree/mast ...


實驗目的:按照一定規律生成類地行星地表地形區塊,並用合理的方式將地形塊顯示出來

涉及知識:Babylon.js引擎應用、著色器編程、正態分佈、數據處理、canvas像素操作

github地址:https://github.com/ljzc002/ljzc002.github.io/tree/master/DataWar

一、在球體網格上顯示紋理的傳統方法:

1、常見的一種星球錶面繪製方法是這樣的:

首先用三角形近似的模擬一個球體網格:

這個簡單場景的代碼如下:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>演示球體的繪製</title>
 6     <link href="../../CSS/newland.css" rel="stylesheet">
 7     <link href="../../CSS/stat.css" rel="stylesheet">
 8     <script src="../../JS/LIB/babylon.32.all.max.js"></script>
 9     <script src="../../JS/LIB/stat.js"></script>
10 </head>
11 <body>
12 <div id="div_allbase">
13     <canvas id="renderCanvas"></canvas>
14     <div id="fps" style="z-index: 301;"></div>
15 </div>
16 </body>
17 <script>
18     var canvas,engine,scene,gl;
19     canvas = document.getElementById("renderCanvas");
20     engine = new BABYLON.Engine(canvas, true);
21     gl=engine._gl;//決定在這裡結合使用原生OpenGL和Babylon.js;
22     scene = new BABYLON.Scene(engine);
23     var divFps = document.getElementById("fps");
24     //全局對象
25     var light0//全局光源
26             ,camera0//主相機
27             ;
28     window.onload=webGLStart;
29     window.addEventListener("resize", function () {
30         engine.resize();
31     });
32     function webGLStart()
33     {
34         gl=engine._gl;
35         createScene();
36         MyBeforeRender();
37     }
38     var createScene = function (engine) {
39         camera0 =new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, -20), scene);
40         camera0.attachControl(canvas, true);
41         camera0.speed=0.5;
42         camera0.minZ=0.0001;
43         light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);
44         sphere1=BABYLON.MeshBuilder.CreateSphere("sphere1",{segments:10,diameter:10.0},scene);
45         var groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
46         groundMaterial.wireframe=true;
47         sphere1.material=groundMaterial;
48 
49     }
50     function MyBeforeRender()
51     {
52         scene.registerBeforeRender(function() {
53             if(scene.isReady())
54             {
55 
56             }
57         });
58         engine.runRenderLoop(function () {
59             engine.hideLoadingUI();
60             if (divFps) {
61                 // Fps
62                 divFps.innerHTML = engine.getFps().toFixed() + " fps";
63             }
64             scene.render();
65         });
66 
67     }
68 </script>
69 </html>
View Code

然後將一張紋理貼圖的紋理坐標對應到球體網格中的每個三角形上,具體原理如下:

以上內容引用自吳亞峰著《OpenGLES3.x游戲開發》,Babylon.js中的紋理對應規則可以參考https://www.cnblogs.com/ljzc002/p/6884252.html中的代碼。

但是這種繪製方式存在以下幾個缺點:

a、為了將二維的圖片映射到三維的球面上,圖片或者紋理坐標必須經過複雜的“拓撲變換”(比如圖中的南極洲明顯經過了拉伸變換),這導致我們在行星錶面點擊一個點時,很難直觀的將它對應到圖片上的某個像素,同時生成適合球面的圖片也需要使用專門的工具進行計算。

b、如果把每個三角形作為一個可交互對象,極地區域的可交互對象將過於密集,想象一個回合制戰棋游戲,玩家會發現極地區域的格子太密而赤道附近的格子太稀疏。

c、在畫面拉近時紋理貼圖會因為信息不足出現不受控制的模糊或變形,當然,我們可以在視角拉近時用更多的細節貼圖來提供更多的信息,但那就是一個更浩大的工程了。

為了避開上述缺點,我決定採用另一種球體紋理繪製方式。

二、使用自定義著色器繪製自定義紋理:

1、在Babylon.js引擎中使用自定義著色器:

Babylon.js通過“著色器材質”對象提供對自定義著色器的支持:

 1 var amigaMaterial = new BABYLON.ShaderMaterial("amiga", scene,{
 2                         vertexElement: "sh1v4.sh",
 3                         fragmentElement: "sh1f4.sh",
 4                     },
 5                     {
 6                         attributes: ["position"],
 7                         uniforms: ["worldViewProjection","worldView"]
 8                     });
 9             amigaMaterial.setVector4("uColor", new BABYLON.Vector4(0.0,1.0,0.0,1.0));
10             sphere1.material=amigaMaterial;

其中ShaderMaterial構造方法的第一個參數是材質名稱、第二個參數是場景對象、第三個參數標明瞭頂點著色器和片元著色器的文件名稱,參考Babylon.js源碼可以看到引擎支持的幾種著色器代碼對象命名方式:

 1 Effect.prototype._loadFragmentShader = function (fragment, callback) {
 2             // DOM element ?著色器代碼是DOM標簽中的內容
 3             if (fragment instanceof HTMLElement) {
 4                 var fragmentCode = BABYLON.Tools.GetDOMTextContent(fragment);
 5                 callback(fragmentCode);
 6                 return;
 7             }
 8             // Base64 encoded ?著色器代碼使用了base64編碼
 9             if (fragment.substr(0, 7) === "base64:") {
10                 var fragmentBinary = window.atob(fragment.substr(7));
11                 callback(fragmentBinary);
12                 return;
13             }
14             // Is in local store ?著色器代碼在Babylon.js自帶的著色器代碼庫里
15             if (Effect.ShadersStore[fragment + "PixelShader"]) {
16                 callback(Effect.ShadersStore[fragment + "PixelShader"]);
17                 return;
18             }
19             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
20                 callback(Effect.ShadersStore[fragment + "FragmentShader"]);
21                 return;
22             }
23             var fragmentShaderUrl;//著色器代碼是一個單獨的文件,需要通過Ajax載入
24             if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
25                 fragmentShaderUrl = fragment;
26             }
27             else {//預設情況下Engine.ShadersRepository = "src/Shaders/";
28                 fragmentShaderUrl = BABYLON.Engine.ShadersRepository + fragment;
29             }
30             // Fragment shader
31             BABYLON.Tools.LoadFile(fragmentShaderUrl + ".fragment.fx", callback);
32         };

我選擇了最後一種方式,將著色器文件放在/src/Shaders/下麵,不要忘記給著色器文件添加尾碼:

 第四個參數是需要Babylon.js從記憶體向顯卡傳遞的“預設”變數,其中attributes里是Babylon.js的各種預設的頂點數據,可以選擇Mesh.geometry._vertexBuffers里的以下幾種頂點數據傳給著色器:

這裡我只選擇了將每個頂點的位置傳遞給著色器,Babylon.js引擎替我們進行了編譯鏈接著色器程式、綁定緩存等一系列操作(Babylon.js中以“_”開頭的變數一般都是在渲染過程中建立的,只有在渲染開始後才有值)。

假設一個網格有1000個頂點,那麼這1000個頂點的位置數據將被分別發送到顯卡上的1000個頂點著色器中,每個著色器使用收到的頂點數據進行計算。

uniforms里是Babylon.js向顯卡發送的預設通用變數,其中world對應網格的變換矩陣,View是相機的變換矩陣,Projection是相機的投影矩陣,worldViewProjection是三個矩陣變換的合併(關於矩陣變換可以參考https://www.cnblogs.com/ljzc002/p/8927221.html中的介紹,或者查看我在B站上傳的3D編程入門視頻教程https://space.bilibili.com/25346426/#/)

對於所有的著色器uniforms型數據都是通用的,比如上面提到的1000個頂點著色器都會使用相同的"worldViewProjection"和"worldView"變數。attributes和uniforms都屬於OpenGL的“存儲限定符”。

第九行代碼設定了一個非預設的uniforms型變數,第十行將這個材質交給球體網格。

2、WebGL版本選擇:

在進行glsl編程之前,一個重要的步驟是選擇要使用的WebGL版本:

OpenGL發展歷史如下:(源文件地址:https://docs.qq.com/sheet/B8uRgG1gE9T32RzNoW38xEnX2epfOY1cwvqG3)

可見WebGL1.0對應早期的OpenGL2.x,WebGL2.0對應較新的OpenGL4.x,顯然WebGL2.0的功能更為強大,但考慮到我的筆記本顯卡不支持WebGL2.0,只好使用舊的WebGL1.0。本文後面的glsl編程均使用OpenGL2.0的語法,OpenGL2.0存在很多缺陷,所以後面的部分內容也正是為瞭解決這些缺陷而編寫的。

Babylon.js可以自動檢測電腦支持的WebGL版本,並優先使用最新版:

 1 // GL
 2             if (!options.disableWebGL2Support) {
 3                 try {
 4                     this._gl = (canvas.getContext("webgl2", options) || canvas.getContext("experimental-webgl2", options));
 5                     if (this._gl) {
 6                         this._webGLVersion = 2.0;
 7                     }
 8                 }
 9                 catch (e) {
10                     // Do nothing
11                 }
12             }
13             if (!this._gl) {
14                 if (!canvas) {
15                     throw new Error("The provided canvas is null or undefined.");
16                 }
17                 try {
18                     this._gl = (canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options));
19                 }
20                 catch (e) {
21                     throw new Error("WebGL not supported");
22                 }
23             }
24             if (!this._gl) {
25                 throw new Error("WebGL not supported");
26             }

Babylon.js源碼里包括很多實用的3D編程工具,即使不使用Babylon.js引擎也可以使用其中的工具簡化原生WebGL開發。

3、簡單glsl代碼:

 測試用的頂點著色器代碼:

 1 uniform mat4 worldViewProjection;
 2 uniform mat4 worldView;
 3 attribute vec3 position;
 4 
 5 varying vec3 vPosition;
 6 
 7 void main(){
 8     gl_Position=worldViewProjection*vec4(position,1);    
 9     vPosition=vec3(worldView*vec4(position,1));
10 }

這裡varying是WebGL1.0中的第三種存儲限定符,它表示這個變數是頂點著色器的計算結果,經過插值後傳入片元著色器(關於WegGL1.0基礎知識,推薦觀看我以前發佈的3D編程入門教程)

gl_Position是OpenGL的內置變數,表示這個頂點經過各種矩陣變換之後在視口中渲染的位置,vPositon指頂點相對於相機的位置。

需要註意的是:除構造函數外,glsl不支持浮點數和整形數之間的自動轉換,浮點數通過“int i=int(f)”轉為整形數,整形數通過"float f=float(i)"轉換為浮點數,上述代碼中的vec4()和vec3()則分別是四元浮點數組和三元浮點數組的構造函數,另外WebGL1.0不具備內置的四捨五入函數,需要使用“floor(f+0.5)”代替四捨五入,並且四捨五入之後仍然是浮點數而非整形數。

除了數組的索引外,著色器中絕大部分的計算都是浮點計算,而將整形計算的結果作為數組索引時也會遇到問題,後面會詳細討論如何處理這一問題。

片元著色器代碼:

 1 precision mediump float;
 2 varying vec3 vPosition;
 3 uniform vec4 uColor;
 4 void main()
 5 {
 6     vec4 tempColor=uColor;
 7     //對2取模,參數必須是浮點型
 8     if(mod((vPosition.x+vPosition.y+vPosition.z),2.0)>1.0)
 9     {
10         tempColor+=vec4(0.0,-0.4,0.0,0.0);
11     }
12     gl_FragColor=tempColor;
13 }

gl_FragColor是一個內置變數,表示片元的最終顏色,註意glsl中的顏色值從0.0到1.0,而不是html中的0到255。

執行代碼效果如下:

可見,隨著相機的移動,球體的紋理自動發生變化,這類效果是很難用貼圖方式實現的。

三、生成並保存簡單的棋盤地形

假設行星的周長為40000km,將每個地塊設為長寬均為100km的正方形,生成並保存一個包含50000多個地塊的棋盤型地面:

1、數據保存:

考慮到每個地塊都要具有獨立的交互能力,使用文件方式保存效率極低,嘗試了html5的本地存儲功能,發現Chrome瀏覽器的本地存儲空間只有5M,難以支持計劃中的對多個行星數據的保存,最終決定使用h2微型資料庫保存地塊數據(讀者可以自己搜索關於h2資料庫的知識,我的視頻教程里也提到了部分相關知識)。

a、在資料庫中建表:

將行星想象為一個一半在地上一半在地下的建築,不同的緯度對應了不同的層數,每一層有若幹個大小相同的房間

 1 --建立地區塊表
 2 create table tab_dqk (
 3 ID varchar(40) NOT NULL,
 4 planetid varchar(40),
 5 beta double,
 6 pbeta double,
 7 alpha double,
 8 palpha double,
 9 weight varchar(1000)
10 );
11 comment on table tab_dqk is '地區塊表';
12 comment on column tab_dqk.id is '主鍵ID';
13 comment on column tab_dqk.planetid is '地區塊所屬的行星id';
14 comment on column tab_dqk.beta is '地區塊的仰角';
15 comment on column tab_dqk.pbeta is '地區塊仰角的區分度';--即這個beta仰角上下pbeta弧度都屬於這一層
16 comment on column tab_dqk.alpha is '地區塊水平轉角';
17 comment on column tab_dqk.palpha is '地區塊水平轉角的區分度';--即這個alpha水平轉角左右palpha弧度都屬於這個房間
18 comment on column tab_dqk.weight is '用JSON表示的地形類型id權重';
19 
20 alter table tab_dqk add column floor int;
21 alter table tab_dqk add column room int;
22 alter table tab_dqk add column altitude double;
23 
24 comment on column tab_dqk.floor is '地區塊位於第幾層';
25 comment on column tab_dqk.room is '地區塊位於這一層的第幾個房間';
26 comment on column tab_dqk.altitude is '地區塊的海拔高度';
27 comment on column tab_dqk.weight is '地區塊類型';
 1 --建立行星表
 2 create table tab_planet
 3 (
 4 id varchar(40) NOT NULL,
 5 name varchar(20) NOT NULL,
 6 coreid varchar(40),
 7 min_floor int NOT NULL,
 8 max_floor int NOT NULL,
 9 width_room int NOT NULL,
10 radius double,
11 mass double,
12 gravity double,
13 orbit double,
14 cycle double
15 );
16 comment on table tab_planet is '行星表';
17 comment on column tab_planet.id is '主鍵ID';
18 comment on column tab_planet.name is '行星名字';
19 comment on column tab_planet.coreid is '圍繞旋轉的主星id';
20 comment on column tab_planet.min_floor is '最低層數';
21 comment on column tab_planet.max_floor is '最高層數';
22 comment on column tab_planet.width_room is '數據寬度';
23 comment on column tab_planet.radius is '半徑(km)';
24 comment on column tab_planet.mass is '質量(t)';
25 comment on column tab_planet.gravity is '重力加速度';
26 comment on column tab_planet.orbit is '同步軌道高度';
27 comment on column tab_planet.cycle is '自轉周期';
28 
29 alter table tab_planet add column perimeter int;
30 
31 comment on column tab_planet.perimeter is '行星周長';

b、dao實現

傳統MVC思想認為瀏覽器端的安全沒有保證,必須在瀏覽器和資料庫之間加入一種“後臺程式”來提高安全性,這種程式通常由JAVA、C#實現,近些年也出現了許多由python和JavaScript實現的後臺程式。這些後臺程式一般包括三層:負責接收瀏覽器訪問的service層、負責將特定訪問參數和數據關聯起來的application層,負責訪問資料庫的dao層。

但是我認為這個實驗中的所有參與者都是可信任的,所以為了程式的簡潔要嘗試去掉後臺程式,我發現h2資料庫服務支持http協議通信,通過使用Fiddler對h2的網頁控制台進行抓包,編寫了直接用瀏覽器和資料庫通信的代碼:(代碼在Linkh2.js中)

 1 /**
 2  * Created by lz on 2018/5/15.
 3  */
 4 var jsessionid="";
 5 var Url="";
 6 var UrlHead="http://127.0.0.1:8082/";
 7 var H2State="offline";
 8 var H2LoginCallback;//回調函數對象
 9 function H2Login(func)
10 {
11     H2LoginCallback=func;
12     Url=UrlHead+"";
13     Argv="";
14     Request(xmlHttp,"POST",Url,true,Argv,"application/x-www-form-urlencoded",H2LoginCallBack,0);
15 }
16 function H2LoginCallBack()
17 {
18     if(xmlHttp.readyState==4) {
19         if(isTimout=="1")
20         {
21             alert("登陸驗證請求超時!!");
22             clearTimeout(timer);
23             xmlHttp.abort();
24         }
25         else {
26             if (xmlHttp.status == 200) {
27                 clearTimeout(timer);//停止定時器
28                 try
29                 {
30                     var str_id=xmlHttp.responseText;
31                     xmlHttp.abort();
32                     jsessionid=str_id.substr(str_id.search(/jsessionid/)+11,32) ;//從h2獲取一個jsessionid
33                     console.log(jsessionid);
34                     H2Login2();
35                 }catch(e)
36                 {
37                     alert(e);
38                     console.error(e)
39                     xmlHttp.abort();
40                 }
41             }
42         }
43     }
44 }
45 function H2Login2()
46 {
47     Url=UrlHead+"login.do?jsessionid="+jsessionid;//用這個jsessionid登錄
48     Argv="language=en&setting=Generic H2 (Embedded)&name=Generic H2 (Embedded)" +
49         "&driver=org.h2.Driver&url=jdbc:h2:tcp://127.0.0.1/../../datawar" +
50         "&user=datawar&password=datawar";
51     Request(xmlHttp,"POST",Url,true,Argv,"application/x-www-form-urlencoded",H2Login2CallBack,0);
52 }
53 function H2Login2CallBack()
54 {
55     if(xmlHttp.readyState==4) {
56         if(isTimout=="1")
57         {
58             alert("登陸驗證請求超時!!");
59             clearTimeout(timer);
60             xmlHttp.abort();
61         }
62         else {
63             if (xmlHttp.status == 200) {
64                 clearTimeout(timer);//停止定時器
65                 try
66                 {
67                     var str_logres=xmlHttp.responseText;//這時已經在h2服務端建立登錄狀態
68                     xmlHttp.abort();
69                     console.log("完成h2資料庫登錄");
70                     H2State="online";
71                     //Query();
72                     //CreateChess();//測試時將運算啟動放在這裡,實際使用時,通過渲染迴圈檢測H2State標誌來啟動運算
73                     H2LoginCallback();//這樣可以執行函數對象嗎????《-可以
74                 }catch(e)
75                 {
76                     alert(e);
77                     console.error(e)
78                     xmlHttp.abort();
79                 }
80             }
81         }
82     }
83 }

其中“Request”是一個Ajax請求函數,內容如下:

 1 /**
 2 	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • #########################################################################介紹Node.js Stream(流)Stream 是一個抽象介面,Node 中有很多對象實現了這個介面。例如,對http 伺服器發起請求的request ...
  • 店鋪裝修-PC端-基礎頁-首頁-裝修頁面:編輯“菜單”模塊-顯示設置,粘貼如下CSS: 自定義導航示例:https://hotshop.bbs.taobao.com/detail.html?postId=7000589 進入CSS導航線上生成工具頁面(http://taobaokaidian.com ...
  • 簡單選擇器 類 id 元素/標簽 * 複合(sel1,sel2)逗號隔開 層次選擇器 s1 s2;後代選擇器,空格隔開 p>c;子代選擇器;不包括孫代及以下 p+next ;相鄰選擇器 p~sub;兄弟選擇器,同一父級下併列子代 表單選擇器,表單form下元素 :input;包括 input,tex ...
  • 一款瀏覽器仿EXCEL純JS表格控制項,純國產化,支持雙擊編輯、設置公式、設置顯示小數精度、下拉框、自定義單元格、複製粘貼、不連續選定、合併單元格、隱藏列、鍵盤操作等。 ...
  • 請你設計出以下圖片里的這個樣式的表格 步驟: 2.將table里的cellspacing設置成0 外邊距是不見了,但是和我們想要完成的圖片有一定的差距,我們發現這樣做出來的圖片好像是兩條線合併到了一起一樣,實際上確實是兩條線合併到了一起的,它是將每個單元格的邊距和表格的邊距重疊到了一起才有的這個圖形 ...
  • 1.什麼是列表標簽? 列表標簽的作用:給一堆數據添加列表語義,也就是告訴搜索引擎,告訴瀏覽器這一堆數據是一個整體 2.HTML列表標簽的分類 2.1無序列表(企業開發中用到最多)(unordered list) 2.2有序列表 (企業開發中用的最少) (ordered list) 2.3定義列表 ( ...
  • github源碼地址 https://github.com/ghshuo/webpack demo webpack介紹 webpack 是一個現代 JavaScript 應用程式的靜態模塊打包器(module bundler)。當 webpack 處理應用程式時,它會遞歸地構建一個依賴關係圖(dep ...
  • 單選按鈕: 加checked=checked屬性 覆選框 加checked=checked屬性 select下拉框 加selected=selected屬性 date日期: value='2018-06-06' 普通文本框: value="你要預設展示的值" textarea: 沒有value屬性, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...