圖片尺寸調整工具

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) → 降低亮度!

Shader Graph 筆記大綱

  1. Shader Graph 的 Space:在 Shader Graph 中,Space(空間)是指資料在不同坐標系統中的表示方式。不同的空間決定了資料如何被處理和顯示,對於著色器的效果有著重要的影響。Shader Graph 中常見的空間類型有:Object Space、World Space、View/Camera Space、Tangent Space 和 Clip Space。
  2. Shader Graph Block Nodes:在 Shader Graph 中,Block Nodes(區塊節點)是組成著色器的基本元素。每個區塊節點代表著一個特定的資料處理功能,這些區塊節點可以在 Master Stack 中組合,最終輸出到著色器的結果。這些節點會處理像是頂點、法線、顏色、光照等屬性,並控制著色器的行為。
  3. Shader Graph Properties : 在 Shader Graph 中,Properties (屬性)是用來設定和控制著色器的外部變量,這些變量可以從 Unity 的材質面板中進行調整,並影響著色器的輸出。這些屬性讓你在不修改 Shader Graph 設定的情況下,動態改變材質的外觀。它們通常用來創建可調整的材質效果,並且可以進行繫結到 Shader Graph 中的不同節點,以控制最終渲染結果。
  4. Shader Graph Nodes :Shader Graph Nodes 是 Shader Graph 中的組件,每個節點都有特定的功能,並且可以連接到其他節點來實現所需的效果。這些節點提供了創建圖形效果所需的數學計算、變換、顏色處理、貼圖采樣等操作。
  5. Shader Graph 入門 : 這篇教學使用 Shader Graph 建立了一個簡單的 Shader,讓 Sprite Renderer 在使用 Material 的時候,仍然可以用 Color 影響顏色。