Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

圖片尺寸調整工具

Godot 中使用 地形 (Terrain)

Godot 4 提供了「地形 (Terrain)」系統,用來自動處理這類圖塊之間的連接。
這能讓你在繪製地圖時,自動使用「正確」的圖塊變體,而不需要手動切換。

地形(Terrains)系統是由 地形集 (Terrain Sets)、地形 (Terrains) 和 圖塊 (Tiles) 所組成的。

  • 一個 TileSet 可以包含一個或多個 地形集 (Terrain Sets);
  • 每個 地形集(Terrain Sets) 可以包含一個或多個 地形 (Terrains);
  • 而每個 地形(Terrains) 又可以包含一個或多個 圖塊 (Tiles)。

地形圖塊(Terrain Tile)具有一個 中心位 (Center Bit) 和多個 周圍偵測位 (Peering Bits)。
每個位元 (bit) 都可以被指定為某一種地形(Terrain)。
我們通常把「某個圖塊(Tile)的中心與相鄰位被指定的地形組合」稱為它的 位元遮罩 (bitmask)。

中心位(也就是圖塊本身的地形),代表整個圖塊所屬的地形類型;
而「相鄰位(Peering Bits)」則像拼圖邊緣一樣,決定這個圖塊可以與哪些其他圖塊相鄰拼接。
圖塊的形狀以及所使用的地形模式(Terrain Mode),
會共同決定這個圖塊擁有哪些相鄰位,以及這些相鄰位的外觀與排列方式。


Godot 4 中有三種 Terrain Modes

匹配邊緣模式 (Match Sides)

  • 此模式需要圖塊(Tile)數量為: 16
  • 優點:圖塊 (Til)最容易被設定與使用,
    • 你可以用它們畫出直線straight lines),
    • 也可以畫出轉角(turns)與交叉口(intersections),
    • 或是填滿整塊矩形區域(filled-in rectangles)。
  • 限制:你無法畫出斜向的線條diagonal lines)。此外,當你繪製矩形區域(rectangles)時,無法區別「外轉角(Outside corners)」與「內轉角(Inside corners)」。對於許多圖塊(Tiles)來說,美術設計上會要求內轉角的樣子必須與外轉角不同。如果你的情況是這樣,你將無法繪製比「單一矩形」更複雜的形狀。
  • 周圍偵測位元 (Peering Bits):
    • 在 匹配邊緣模式(Match Sides)下,會使用圖塊 (Tiles)邊緣的周圍偵測位元 (Peering Bits)。
    • 這表示 正方形(Square)和等軸測(Isometric)的圖塊(Tiles)具有四個周圍偵測位元 (Peering Bits);
    • 而六邊形的圖塊(hexagonal tiles)則有六個。
    • 每個周圍偵測位元 (Peering Bits)只會對應到一個鄰近圖塊 (neighboring tile)。位於角落的鄰居則不會被影響。

角對齊模式 (Match Corners)

  • 此模式需要圖塊(Tile)數量為: 16
  • 優點:圖塊 (Tile)最容易被設定與使用,
    • 你可以用它畫出較複雜的形狀,特別適合用於繪製大面積的陸地(landscape) 或是洞穴地形(caves)
  • 限制:圖塊 (Tile)只能以 4 個為一組(2x2 方塊)進行連接。這意味著無法實現細微的細節,也無法繪製寬度僅為單個圖塊 (Tile)的線條。
    • 注意為了避免錯誤,在此模式下,請使用 Rectangle 工具來繪製,並至少保證要四個 tile 在一起,
  • 周圍偵測位元 (Peering Bits):角對齊模式 (Match Corners)在圖塊 (Tile)的頂角(Corners)上使用周圍偵測位元(Peering bits)
    • 方形(Square)和等軸測(Isometric)的圖塊(Tiles): 具有四個周圍偵測位元 (Peering Bits),且每個周圍偵測位元 (Peering Bits)處會有 3 個鄰近圖塊(Tiles)。
    • 六角形 (Hexagonal) 圖塊(Tiles): 擁有 6 個周圍偵測位元 (Peering Bits),但每個周圍偵測位元 (Peering Bits)處只有 2 個鄰近圖塊(Tiles)。

角與邊對齊 (Match Corners and Sides)

  • 此模式需要圖塊(Tile)數量為: 47
  • 優點:最全能的模式,可以做到另外兩個模式能做到的所有形狀
  • 限制:他無法建立對角線(diagonal lines)且他需要最多的圖塊(Tile)。
  • 周圍偵測位元 (Peering Bits):
    • 在此模式下,圖塊 (Tiles)的頂角 (Corners) 與 邊緣 (Edges) 同時使用周圍偵測位元 (Peering Bits)。
    • 這表示 正方形(Square)和等軸測(Isometric)的圖塊(Tiles)具有8個周圍偵測位元 (Peering Bits);
    • 而六邊形的圖塊(hexagonal tiles)擁有 12 個。
    • 在每個「邊」的部分,每個周圍偵測位元 (Peering Bits)只會對應到一個鄰近圖塊 (neighboring tile)。
    • 在「角」的部分,方形與等距圖塊有 3 個鄰居,六角形圖塊則有 2 個鄰居。所有共享同一個頂角的圖塊 (Tiles),其該角的周圍偵測位元 (Peering Bits)必須設置為相同的地形。

如何使用 Rovo Dev CLI

以下紀錄在 Mac 中如何使用 Rovo Dev CLI

首先訪問 https://developer.atlassian.com/cloud/acli/guides/install-macos/

透過 Homebrew 安裝 acli

1
2
brew tap atlassian/homebrew-acli
brew install acli

再到 https://www.atlassian.com/software/rovo-dev 註冊一個帳號
完成後到你的 Atlassian 帳戶後台,找到 https://id.atlassian.com/manage-profile/security/api-tokens 建立權杖(token),目前最多只能設定一年。

有了權杖(token) 之後,開啟終端機,輸入以下登入指令

1
acli rovodev auth login

之後會提示要輸入你的 Email 和以及剛剛產生的 API 權杖(Token)

登入成功後,我們就可以開始使用,終端機位置移動到你程式碼專案的資料夾下,並輸入

1
acli rovodev run

如果一切沒有問題,就可以看到 Rovo 啟動的畫面

1
2
3
4
5
6
7
8
9
10
11
12
╭────────────────────────────────────────────╮
│ ⬢ Rovo Dev │
╰────────────────────────────────────────────╯

Working in /Volumes/test/git/your_project

Using model: auto

╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > █ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Type "/" for available commands or "#" for file search. Uses AI. Verify results.

在執行的過程中,如果 Rovo 需要執行需要權限的指令 (如 mkdir) ,它都會詢問你的權限。

  • Allow (ones): 僅同意這一次。
  • Allow (session): 在這個 session 中永遠同意此操作。
  • Allow (always): 永遠允許此操作。
  • Deny (ones): 拒絕這一次。
  • Deny (session): 在這個 session 中永遠Deny此操作。
  • Deny (always): 永遠Deny此操作。

如果想更改,可以到 /Users/使用者/.rovodev/config.yml 檔案中修改

Chrome 播放影片時,顯示即時字幕

有時候英文影片沒有字幕,我們可以使用 Chrome 來播放它,並產生即時字幕。
這個功能預設是不開啟的,以下說明如何開啟。

在 Chrome 瀏覽器位置輸入 chrome://settings ,進入設定畫面,找到 無障礙設定
無障礙設定 中的 即時字幕 開啟,他會自動下載語言,你可以選擇添加自己想要的語言。

之後,將影片檔案拖入到 Chrome 中,它就會為你即時產生字幕了。

LTX-Video Test

以下紀錄使用 LTX-Video 的筆記

https://github.com/Lightricks/LTX-Video

首先要將它從 GitHub 拉下來

1
git clone https://github.com/Lightricks/LTX-Video.git

切換到他的資料夾下

1
cd LTX-Video

建立 Python 虛擬環境

1
2
3
python -m venv env
source env/bin/activate
python -m pip install -e .\[inference-script\]

前往 https://huggingface.co/Lightricks/LTX-Video 下載 Model

使用 文字生成影片

1
python inference.py --ckpt_path /Volumes/test/video/ltx-video-2b-v0.9.1.safetensors --prompt "A monkey dance" --height 768 --width 1024 --num_frames 10 --seed 2

使用 圖片生成影片

1
python inference.py --ckpt_path /Volumes/test/video/ltx-video-2b-v0.9.1.safetensors --prompt "A monkey dance" --input_image_path /Volumes/test/video/8e5352fc-70b0-41cd-9a9a-704445df7ab0.png --height 768 --width 1024 --num_frames 200 --frame_rate 20 --seed 3

Mac 使用 ComfyUI

以下紀錄在 Mac 上安裝 ComfyUI 的步驟

  1. 安裝 Homebrew
    • /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    • https://brew.sh/zh-tw/
  2. 安裝 Python
    • brew install cmake protobuf rust python@3.10 git wget
  3. 切換到你想放 ComfyUI 的位置,在執行 git clone
  4. 切換到下載來 ConfyUI 的位置
    • cd ComfyUI
  5. 執行 Python
    • python3 -m venv venv
  6. 執行以下指令安裝
    • ./venv/bin/pip install torch torchvision torchaudio
    • ./venv/bin/pip install -r requirements.txt
  7. 下載模型
  8. 下載下來的檔案副檔名可能是 .ckpt.safetensors
  9. 把下載完畢的模型放到 ComfyUI 中的 /models/checkpoints 的路徑
    • /ComfyUI/models/checkpoints
  10. 啟動 ComfyUI
    • ./venv/bin/python main.py

若想與 AUTOMATIC1111 Stable Diffusion WebUI 一起使用相同的模型的話,可以找到 extra_model_paths.yaml.example 這個檔案,他應該直接在 ComfyUI 的目錄下。
將檔案複製一份,並更改檔名為 extra_model_paths.yaml
打開後會看到

1
2
a111:
base_path: path/to/stable-diffusion-webui/

把 base_path: 更改為你AUTOMATIC1111 Stable Diffusion WebUI的位置。

1
2
a111:
base_path: /Volumes/test/stable-diffusion-webui/

最後重新啟動 ComfyUI 就可以了。


想更新的話,在資料夾中執行 git pull,然後在執行 ./venv/bin/python main.py 即可

Spring Console Line App

以下紀錄如何使用 Spring Boot 作為 Console Line App
Spring Boot 的版本是 3.4.2

在 application.properties 中加入 spring.main.web-application-type=NONE , 告訴 Spring Boot 不啟動 Web 環境

1
spring.main.web-application-type=NONE

新增一個實作 CommandLineRunner 的類,並標記上 @Component
之後你就可以在這個類的 run() 方法中寫你的 Console Line App 邏輯並使用 Spring 的依賴注入了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class AppCommandLineRunner implements CommandLineRunner {

Logger logger = LoggerFactory.getLogger(AppCommandLineRunner.class);


@Autowired
private SomeService service;

@Override
public void run(String... args) throws Exception {

logger.info("run the app, args="+ Arrays.toString(args));

service.do();
}

}

如果你每次在執行 Console Line App 時,不想要顯示 Spring banner 的訊息的話,可以在 application.properties 中加入

1
spring.main.banner-mode=off

Java 時間

GMT(Greenwich Mean Time): GMT 是格林威治標準時間,基於地球自轉,定義為通過倫敦格林威治天文台的子午線(本初子午線)的時間。
UTC(Coordinated Universal Time):UTC 是全球協調時間,基於原子鐘的精確計時,與 GMT 基本一致。
在 Java 等程式語言中,通常使用 UTC 作為標準時間,如 Instant.now() 獲取的是 UTC 時間。

台灣的時區是 GMT+8,這表示台灣的時間比格林威治標準時間(GMT)快 8 小時
而英國的時區為 GMT+0 ,這意味著英國的時間與 GMT 相同。
而這兩個地區的時間差了 8 小時 。

如果在台灣的時間是 2025年2月19日 15:30(GMT+8),那麼同一個時間裡,在英國(GMT+0)看到的時間會是 2025年2月19日 15:30 - 8小時 = 2025年2月19日 07:30

Timestamp(時間戳) 是一個整數,代表著從 UTC 1970 年 1 月 1 日 0 時 0 分 0 秒 起至現在的總秒數。

  • 在 Java 中使用 System.currentTimeMillis() 取得 Timestamp

Java 8 以前, 使用 Date ,而 Date 有以下缺陷,不建議使用

  • 是可變的(mutable),是執行緒不安全的
  • 時區 和 Timestamp 混在一起,會因不同的電腦系統導致顯示不同時間。
  • 若要處理時區需借助 SimpleDateFormat 或其他 Library 輔助。
    • SimpleDateFormat 是執行緒不安全的
  • 日期與時間沒有分開,無法只單獨處理時間或是日期。
    • 你只想要日期的話,在 Date 物件中仍然會有時間的部分,它會顯示為 2025-02-06 00:00:00

Java 8 推出了 java.time

無時區的日期時間

LocalDate

  • LocalDate 代表日期,只儲存了年、月、日。
  • 是不可變的(immutable)
1
2
3
4
5
6
7
8
9
10
LocalDate date = LocalDate.now();
System.out.println(date1); // 2025-02-06

date = LocalDate.of(2025, 2, 6);
// 由於是不可變的,因此透過重新賦值來更改。
date = date.plusDays(5); // 增加 5 天
System.out.println(date.getYear()); // 2025
System.out.println(date.getMonth()); // FEBRUARY
System.out.println(date.getMonthValue()); // 2
System.out.println(date.getDayOfMonth()); // 11

LocalTime

  • 代表時間,只儲存時間,時、分、秒,此外,也儲存奈秒。
  • 是不可變的(immutable)
1
2
3
4
5
6
7
LocalTime time = LocalTime.now();
System.out.println(time); // 15:40:54.204743

time = time.minusSeconds(5); // 減少 5 秒
System.out.println(time.getHour()); // 15
System.out.println(time.getMinute()); // 40
System.out.println(time.getSecond()); // 49

LocalDateTime

  • 封裝了 LocalDateLocalTime 代表日期時間
  • 是不可變的(immutable)
1
2
3
4
5
6
7
8
9
10
11
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime); // 2025-02-19T15:43:26.124154

dateTime = dateTime.plusDays(30);

System.out.println(dateTime.getYear()); // 2025
System.out.println(dateTime.getMonthValue()); // 3
System.out.println(dateTime.getDayOfMonth()); // 21
System.out.println(dateTime.getHour()); // 15
System.out.println(dateTime.getMinute()); // 43
System.out.println(dateTime.getSecond()); // 26

DateTimeFormatter

  • 和 SimpleDateFormat 類似,用來進行日期時間物件,與字串之間的互換,但不用處理 ParseException 這個 checked exception。
1
2
3
4
5
var formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");

LocalDateTime dateTime = LocalDateTime.parse("2025/02/06 01:02:03", formatter);
System.out.println(dateTime); // 2025-02-06T01:02:03
System.out.println(formatter.format(dateTime)); // 2025/02/06 01:02:03

有時區的日期時間

ZonedDateTime

  • 有時區的日期時間

ZoneId

  • 可以使用 ZoneId.getAvailableZoneIds() 取得支援的 key 值。
1
2
3
4
5
6
7
8
ZonedDateTime now = ZonedDateTime.now();
System.out.println("Current ZonedDateTime: " + now); // Current ZonedDateTime: 2025-02-19T15:56:36.652708+08:00[Asia/Taipei]

ZonedDateTime nowInUTC = ZonedDateTime.now(ZoneId.of("UTC"));
System.out.println("Current time in UTC: " + nowInUTC); // Current time in UTC: 2025-02-19T07:56:36.652850Z[UTC]

ZonedDateTime nowInTaipei = ZonedDateTime.now(ZoneId.of("Asia/Taipei"));
System.out.println("Current time in Taipei: " + nowInTaipei); // Current time in Taipei: 2025-02-19T15:56:36.652881+08:00[Asia/Taipei]

亦可直接使用偏移量即 UTC/GMT,來定義 ZoneId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var dateTime = LocalDateTime.now();

// 定義東京的 ZoneId (UTC+9)
var tokyoZoneId = ZoneId.of("+0900");
var tokyoDateTime = ZonedDateTime.of(dateTime, tokyoZoneId);

// 定義倫敦的 ZoneId (UTC+0)
var londonZoneId = ZoneId.of("+0000");
var londonDateTime = tokyoDateTime.withZoneSameInstant(londonZoneId);

// 輸出東京時間
System.out.println(tokyoDateTime); // 2025-02-19T16:04:38.833229+09:00
// 輸出倫敦時間
System.out.println(londonDateTime); // 2025-02-19T07:04:38.833229Z


參考

Shader Graph 入門

以下將使用 Shader Graph 建立一個簡單的 Shader , 這個 Shader 能夠正常接收 Sprite Renderer 的 Sprite,並允許透過 Sprite Renderer 的 Color 來調整顏色。

  1. 建立 Shader Graph : 在 Unity,前往 Assets 資料夾,右鍵點擊 → Create → Shader Graph → Unlit Shader Graph。命名 為 Color Shader Graph。並雙擊打開 Shader Graph 編輯器。
  2. 在 Shader Graph 中,我們需要一個 Texture2D 屬性
    • 在左側的面板上,找到 + , 點擊 + 並選擇 Texture2D,將其命名(Name)為 MainTex。
    • 命名為 MainTex , 會發現 Reference 變為 _MainTex。這樣當這個 Shader 被應用到 Sprite Renderer 的 Material 上時,Sprite Renderer 會自動把它的 Sprite 貼圖傳遞給 _MainTex。這樣 Shader 就能正確使用 Sprite 的貼圖,而不需要手動指定 Texture。
  3. 在 Shader Graph 中,我們需要三個 Node
    • Sample Texture 2D : 把建立好的 Texture2D 屬性 (_MainTex) 輸入到這個 Sample Texture 2D 節點的 Texture 2D 。
    • Vertex Color : 這個節點會讀取 Sprite Renderer 設定的 Color 。
    • Multiply : 建立一個 後。
      • 將 Sample Texture 2D 的輸出(RGBA) 連接到 Multiply(A 輸入)
      • 將 Vertex Color 的輸出(RGBA) 連接到 Multiply(B 輸入)
  4. 最後將 Multiply 的輸出 連接到 Fragment Output 的 Base Color。
  5. 建立一個 Material , 將他的 Shader 設為在上面的 Shader Graph 。
  6. 建立一個 Sprite Renderer , 指定一個 Sprite , 並賦予剛剛建立的 Material
  7. 調整 Sprite Renderer 中的 Color 看看是否有變更顏色。
  8. 可以發現當 Sprite Renderer Color 為白色的時候,呈現的是材質本身的顏色。這就是我們為何使用相乘 (Multiply) 的原因。

為什麼需要建立一個 Vertex Color ?

  • 這是因為 Sprite Renderer 的 Color 其實是「頂點顏色(Vertex Color)」,Unity 會把它當作一個 Color 傳進 Shader。
  • 如果你的 Shader 沒有讀取 Vertex Color,那麼 Sprite Renderer 改變 Color 也不會影響最終顯示的顏色。

為什麼要用相乘 (Multiply)?

  • 因為 Sprite Renderer 的 Color 本質上是「顏色的縮放因子」,影響材質的最終顏色。
  • 所以,Shader 必須把 Texture 的顏色與 Vertex Color 相乘,這樣才能讓 Color 正確影響結果!
  • 假設:
    • Texture Color 是 (1, 1, 1, 1)(白色)
    • Vertex Color 是 (1, 0, 0, 1)(紅色)
    • 那麼,最終顏色 = 𝑇𝑒𝑥𝑡𝑢𝑟𝑒𝐶𝑜𝑙𝑜𝑟 × 𝑉𝑒𝑟𝑡𝑒𝑥𝐶𝑜𝑙𝑜𝑟 = (1,1,1,1)×(1,0,0,1)=(1,0,0,1) → 結果變成紅色!
  • 同理:
    • (0.5, 0.5, 0.5, 1) × (1, 0, 0, 1) = (0.5, 0, 0, 1) → 半透明紅色!
    • (1, 1, 1, 1) × (1, 1, 1, 1) = (1, 1, 1, 1) → 不變!
    • (1, 1, 1, 1) × (0.5, 0.5, 0.5, 1) = (0.5, 0.5, 0.5, 1) → 降低亮度!