How to make a simple fog effect in OpenGL ?

Ok, I’ve just implemented the fog effect while studying OpenGL, so I’m going to show you guys how to do it.

In order to test the effect, you must have object in 3d space. Here mine is a 2×2 texture
(I create a simple 2×2 texture and then mapping it to the 4-vertex square)

texture

My vertex shader file:

// Vertex shader
attribute vec3 a_position;
attribute vec2 a_uv;

uniform mat4 u_projection;
uniform mat4 u_view;
uniform mat4 u_model;

varying vec3 v_position;
varying vec2 v_uv;

void main()
{
vec4 positionTmp = vec4 ( a_position, 1.0 );

v_position = ( u_model * positionTmp ).xyz;
v_uv = a_uv;

gl_Position = u_projection * u_view * u_model * positionTmp;
}

My fragment shader file:

// Fragment shader
precision mediump float;

uniform sampler2D u_texture;
uniform vec4 u_fogColor;
uniform vec4 u_eyePos;
uniform float u_fogMaxDist;
uniform float u_fogMinDist;

varying vec3 v_position;
varying vec2 v_uv;

float eyeDist;

float computeLinearFactor()
{
float factor;

factor = ( u_fogMaxDist - eyeDist ) /
( u_fogMaxDist - u_fogMinDist );
factor = clamp ( factor, 0.0, 1.0 );

return factor;
}
void main()
{
eyeDist = distance ( v_position, u_eyePos );

float factor = computeLinearFactor();
vec4 fogColor = fogFactor * u_fogColor;
vec4 baseColor = texture2D ( u_texture, v_uv );

gl_FragColor = baseColor * fogFactor +
fogColor * ( 1.0 - fogFactor );

Result ( my u_fogColor is red):
texture_fog

Setting environment for NDK ( Cài đặt môi trường cho NDK)


Các công cụ cần thiết
1. Android ndk: https://developer.android.com/ndk/downloads/index.html#Downloads
2. Android sdk: https://developer.android.com/sdk/index.html (Kéo xuống SDK tools only)
3. Apache ant: http://ant.apache.org/
4. Jdk 1.6 trở lên: http://www.oracle.com/technetwork/java/javase/downloads/index.html

Cài đặt Environment Variable
Thêm đường dẫn vào path variable:
<android-ndk_path>;
<apache-ant_path>\bin;
<android-sdk_path>\tools;
<android-sdk_path>\platform-tools;
<jdk_path>\bin

Build ứng dụng mẫu <android-ndk_path>\samples\hello-jni
Step 1: Tạo file build.xml
+ Mở cmd:
root-disk <enter>
cd <android-ndk_path>\samples\hello-jni <enter>
<android-sdk_path>\tools\android update project -t <target-id> -p <android-ndk_path>\samples\hello-jni -s <enter>

Untitled

Untitled

Chú ý: target-id là id tương ứng của từng phiên bản android đã được cài đặt trong SDK, để xem chi tiết, type lệnh sau trong cmd:
<android-sdk_path>\tools\android list targets

Step 2: Tạo file *.so
*.so (Share object file): là file giúp Java có thể đọc được code C++ được dạng trong chúng
+ Mở cmd:
root-disk <enter>
cd <android-ndk_path>\samples\hello-jni <enter>
<android-ndk_path>\ndk-build.cmd <enter>

Untitled

Untitled

Step 3: Build file apk
+ Mở cmd:
root-disk <enter>
cd <android-ndk_path>\samples\hello-jni <enter>
<apache-ant_path>\bin\ant.bat debug<enter>

Chờ khoảng 5s, nếu xuất hiện dòng BUILD SUCCESSFUL thì bạn đã thành công. File apk sẽ được lưu trong thư mục bin của project. Cấu trúc của thư mục bin này cũng giống với cấu trúc của 1 file apk thông thường khi decompile

Chú ý: Đến bước này nếu bị lỗi đường dẫn của SDK không đúng, bạn hãy tìm tới file local.properties trong thư mục <android-ndk_path>\samples\hello-jni, và edit lại đường dẫn của SDK tại biến sdk.dir

Step 4: Chạy thử file apk trên android device
Kéo thả file apk vào genymotion

Untitled
Hello JNI !

Happy coding ! 🙂

GIẢI PHÁP XỬ LÍ NHIỀU MÀN HÌNH TRONG ANDROID – P2

Bài viết này giải thích giải pháp của: Gemserk blog theo cách hiểu của mình.
This post explains Gemserk’s solution: Gemserk blog to handle multiple screen sizes in my own way.


Trong bài này mình muốn các bạn phân biệt rõ 2 khái niệm, đó là RealViewport và VirtualViewport ( VirtualViewport là khái niệm trọng tâm của phương pháp). 2 khái niệm này sẽ được giải thích tại chỗ thích hợp của bài.

Theo Gemserk, chúng ta sẽ xây dựng 3 class đó là: VirtualViewport, MultipleVirtualViewportBuilder, và OrthographicCameraWithVirtualViewport. Vì blog của mình chưa có plugins để chèn code nên các bạn hãy vào blog của Gemserk để đọc code của 3 class này.

MULtiple-virtual-viewport-builder

Class này sẽ chứa thông tin của 2 khoảng kích thước màn hình chúng ta muốn hỗ trợ tốt nhất, theo như bài viết trước, đó là (800 x 480) và (854 x 600), chúng ta lần lượt gán cho các biến (minWidth, minHeight) và (maxWidth, maxHeight) của class này.

Sau đó từ kích thước của thiết bị ( lấy từ Gdx.graphics.getWidth()Gdx.graphics.getHeight()), tương ứng với 2 biến widthheight trong hàm getVirtualViewport(float, foat), nó sẽ tính toán để cho ra kích thước của VirtualViewport (virtualViewportWidth, virtualViewportHeight).

Ghi chú: VirtualViewport là viewport ảo mà chúng ta nên đặt các thành phần động ( như buttons, game title…) ở mép của nó. VirtualViewport sẽ luôn luôn có kích thước nằm trong khoảng của min và max. Và tỉ lệ của VirtualViewport không nhất thiết phải bằng với tỉ lệ của thiết bị.

Giải thích code: sẽ có tất cả 4 trường hợp xảy ra:

1. Nếu widthheight đã nằm gọn trong min và max thì ta sẽ trả về một VirtualViewport với kích thước chính là widthheight luôn.
g2993
Nếu nó không nằm gói gọn trong min và max, thì ta sẽ thử 2 trường hợp tiếp theo, 2 trường hợp này sẽ cho ta tỉ lệ kích thước của VirtualViewport bằng với tỉ lệ của widthheight

2. Cố định virtualViewportWidth theo minWidth
virtualViewportWidth = width * scaleForMinSize = width * (minWidth width) = minWidth.
virtualViewportHeight  = virtualViewportWidth * aspect.
Hàm insideBounds(float, float) cho phép ta kiểm tra xem kích thước VirtualViewport mới tính toán có nằm trong khoảng min và max hay không.
Nếu có thì lúc đó ta sẽ được một VirtualViewport có kích thước là hình chữ nhật nhỏ nhất chứa vùng min, tương tự như thế này:

g2993

Nếu không thì ta đến với trường hợp 3

3. Cố định virtualViewportWidth theo maxWidth:
Như trường hợp 2, giải thích hoàn toàn tương tự.
VirtualViewport lúc này sẽ có dạng như sau:
g2993
Nếu vẫn không nằm trong khoảng min và max thì đến trường hợp 4:

4. Trả về VirtualViewport với kích thước minWidth, minHeight:
VirtualViewport lúc đó chính là hình vuông màu đỏ

Nhận xét:
1. Trong trường hợp 2 và 3, không cố định theo minWidthmaxWidth, mà cố định theo minHeightmaxHeight có được không ? Trả lời: tất nhiên được, code thử xem đê !

2. Trong trường hợp 1, 2 và 3, ta đều nhận được VirtualViewport có tỉ lệ kích thước bằng với tỉ lệ kích thước của thiết bị: virtualViewportWidth / virtualViewportHeight = width / height. Còn trong trường hợp 4 thì không, lúc đó virtualViewportWidth / virtualViewportHeight = minWidth / minHeight

virtual-viewport

Class này có virtualWidthvirtualHeight chính là kích thước của VirtualViewport tính toán được từ class MultipleVirtualViewportBuilder ở trên.
Các hàm getWidth(), getHeight(), sẽ cho ta kích thước của RealViewport, là realWidth và realHeight. Mặc dù trong bài viết của Gemserk không có các tên gọi này nhưng để tiện trong việc giải thích mình đã thêm vào, các bạn chỉ cần biết 2 hàm getWidth()getHeight(), sẽ tả về realWidth và realHeight.

Ghi chú: kích thước của RealViewport chính là kích thước để truyền vào camera, nó chính là 2 biến viewportWidth và viewportHeight của OrthographicCamera mà chúng ta thường dùng. Và kích thước này chính là vùng hiện game lên trên thiết bị của chúng ta. Như vậy, RealViewport phải thỏa mản 2 điều kiện:
+ tỉ lệ realWidth / realHeight phải bằng tỉ lệ của kích thước màn hình thiết bị ( tất nhiên- lúc đó thì hình ảnh game hiện lên trên thiết bị mới giữ đúng tỉ lệ của nó và không bị bóp méo)
+ RealViewport phải chứa VirtualViewport, và kích thước của RealViewport phải làm thế nào đó để nó nhỏ hơn kích thước của vùng max trong càng nhiều trường hợp càng tốt
=> Trường hợp tối ưu đó là: RealViewport phải là hình chữ nhật nhỏ nhất chứa VirtualViewport, và tỉ lệ của RealViewport phải bằng với tỉ lệ của màn hình.

Trước khi đi vào giải thích code, ta xét bài toán sau đây: Cho một hình chữ nhật kích thước bất kì (a, b) và một tỉ lệ k. Ta xây dựng một hình chữ nhật mới (x, y) sao cho nó có tỉ lệ x / y = k, bằng 2 phương pháp sau đây:
Phương pháp 1: Cho x = a => y = x / k = a / k.
Phương pháp 2: Cho y = b => x = y * k = b * k.
Ta có thể chứng minh các điều sau:
+ Luôn luôn có một trường hợp mà ta sẽ nhận được một hình chữ nhật (x, y) có thể chứa hình chữ nhật (a, b). Nếu pp1 tính ra (x, y) không chứa, thì pp2 sẽ chứa – và ngược lại.
+ Lúc đó (x, y) là hình chữ nhật nhỏ nhất chứa (a, b), sao cho x / y = k. ( vì trong 2 phương pháp ta đã gán x hoặc y chính bằng a hoặc b)

Đó chính xác là cách mà Gemserk đã xây dựng RealViewport, (a, b) chính là kích thước của VirtualViewport, k chính là tỉ lệ màn hình aspect = screenWidth / screenHeight, và (x, y) chính là RealViewport cần tìm.

Trong hàm getWidth(), cũng như getHeight(), hãy để ý đến biểu thức điều kiện trong if(…) else, điều kiện đó cũng chính là:
if (aspect >= virtualAspect) {….} else {…}

Do trong nhiều trường hợp ta không thể so sánh chính xác 2 đại lượng float (do cách biểu diễn số thực trong máy tính, google để biết thêm thông tin chi tiết), nên so sánh bằng ở đây được thay bằng một trị tuyệt đối, có nghĩa là: nếu aspect hơn kém virtualAspect một khoảng 0.01f, thì cứ coi như là bằng nhau.

Vậy vì sao phải chia trường hợp như vậy ? Trả lời: để biết là nên dùng phương pháp 1 hay phương pháp 2, trong bài toán ở trên ( xét cho trường hợp a >b sẽ không ảnh hưởng đến tính tổng quát của bài toán), nếu ta có k > a/b thì ta sẽ dùng phương pháp 2, ngược lại thì dùng phương pháp 1, lúc đó ta sẽ nhận được hình chữ nhật thỏa mãn điều kiện. (Phần chứng minh xin dành lại cho bạn đọc).

Cách chia trường hợp như trên có vẻ không tự nhiên để tiếp cận ? Thực ra bạn không cần chia trường hợp như trên, mà chỉ cần code trâu thử lần lượt từng trường hợp (phương pháp) cũng được, nếu làm theo pp1 không thỏa mãn thì làm theo pp2. Ok !

orthographiccamera-with-virtual-viewport

Sửa virtualViewport.getVirtualWidth()  ở hàm update() thành viewportWidth, tương tự cho height

…..

Nhận xét:

// Để đơn giản, Gemserk đã cố định kích thước theo 2 vùng min, max để tìm ra kích thước của VirtualViewport; tại vì khi đã biết một kích thước, và tỉ lệ, thì việc suy ra kích thước còn lại chỉ là một phép toán. Đây chưa phải là cách làm tối ưu ( tất nhiên trình độ của các dev bên Gemserk có thể làm được tốt hơn là cái chắc !), chúng ta có thể xây dựng một thuật toán để nó có thể tìm ra kích thước một vùng VirtualViewport xanh lá cây nằm giữa 2 vùng min max với aspect = width / height đã biết mà không cần dựa vào minWidth hoặc maxWidth như trên. Tức là quy về bài toán tìm (x, y), biết x / y = aspect, minWidth <= x <= maxWidth và minHeight <= y <= maxHeight

 

Giải pháp xử lí nhiều màn hình trong Android – P1

Bài viết được dịch từ: Gemserk blog
This post is translated from: Gemserk blog


Làm game để chạy được trên nhiều thiết bị không phải dễ. Một trong những vấn đề phổ biến gặp phải là làm sao để game chạy được trên nhiều kích thước ( tỉ lệ, độ phân giải) màn hình khác nhau.

Trong bài viết này chúng tôi muốn chia sẻ cách để xử lí vấn đề này khi chúng tôi làm game  Clash of the Olympians cho Android.

Trong mục tiếp theo chúng tôi sẽ chỉ ra một số cách mọi người thường dùng để xử lí nhiều màn hình, và sau đó là cách riêng của chúng tôi.

KÉO DÃN

Một trong những cách tiếp cận vấn đề này là đầu tiên, làm sao cho game hiển thị vừa với độ phân giải màn hình mà bạn nhắm đến. Chẳng hạn cho độ phần giải màn hình là 800 x 480

Theo đó, bạn sẽ có cách bố trí game cho độ phân giải này như sau:

Menu screen của game Clash of the Olympians chạy trên màn hình 800×480.

Sau đó, để hỗ trợ cho những kích thuớc màn hình khác, ta sẽ kéo dãn game ra sao cho vừa với kích thước mới:

Menu screen của game Clash of the Olympians chạy trên màn hình 800×600 ( được kéo dãn từ màn hình 800×480)

Ta có thể nhận thấy vấn đề ở đây là tỉ lệ các hình vẽ trong game đã bị ảnh hưởng và khó có thể chấp nhận được.
(nd: các bạn có thể thấy các chữ, buttons, background… bị kéo dãn ra theo chiều dọc)

KÉO DÃN VÀ GIỮ TỈ LỆ MÀN HÌNH

Để xử lí vấn đề trên, một kĩ thuật phổ biến đó là cũng kéo dãn, nhưng sẽ giử đúng tỉ lệ của các hình vẽ trong game, bằng cách thêm vào các viền màu đen, và như vậy tỉ lệ các hình ảnh sẽ luôn được giữ đúng trên các kích thước màn hình khác nhau. Trông như sau:

Menu screen trên kích thước màn hình 800×600

Menu screen trên kích thước màn hình 854×480

Đây là một cách dễ dàng để xử lí vấn đề nhiều kích thước màn hình, thay vì dùng màu đen, các bạn cũng có thể trang trí cho các thanh viền để game nhìn ổn hơn.

Tuy nhiên, trong một vài trường hợp thì cách này tỏ ra không phù hợp, bởi vì các viền đen đó làm game trông có vẻ không tự nhiên, và làm người chơi có suy nghĩ: game này không phải được làm để chạy tốt trên thiết bị của mình.

GIẢI PHÁP CỦA CHÚNG TÔI: XÂY DỰNG MỘT GÓC NHÌN ẢO (VIRTUAL VIEWPORT)

Our approach consists in adapting what is shown in the game screen area to the device screen size.

Đầu tiên, chúng tôi quyết định xem chúng tôi sẽ hỗ trợ những thiết bị có độ phân giải trong khoảng nào ? Ví dụ trong trường hợp này chúng tôi sẽ chọn tỉ lệ 4:3 (800×600) và 16:9 (854×480), vậy tất cả các màn hình có kích thước nằm trong khoảng đó đều sẽ được hỗ trợ.

Với 2 tỉ lệ đó, chúng ta sẽ qui định diện tích tối đa là 854×600 và diện tích tối thiểu là 800×480. Ý tưởng ở đây đó là tất cả mọi thứ đều phải nằm bên trong diện tích lớn nhất, nhưng những thứ quan trọng ( buttons, information…) phải luôn nằm trong diện tích nhỏ nhất.

Hình chữ nhật màu đỏ thể hiện cho diện tích nhỏ nhất, hình màu xanh biển thể hiện cho vùng diện tích lớn nhất.

Sau đó, với kích thước màn hình đã biết (nd: lấy từ Gdx.graphics.getWidth() và Gdx.graphics.getHeight()), chúng ta tính toán một diện tích có tỉ lệ bằng với tỉ lệ kích thước màn hình, nhưng độ lớn của nó phải nằm giữa 2 phần diện tích đỏ và xanh biển. Ví dụ, với độ phân giải 816×544 (4:3), ta sẽ có:

Hình chữ nhật màu xanh lá cây thể hiện cho vùng diện tích 816×544

Và trên thiết bị có kích thước 816×544, menu screen sẽ trông như thế này

Trong trường hợp kích thước màn hình mà chúng ta muốn hỗ trợ lớn hơn hình chữ nhật max, hoặc nhỏ hơn hình chữ nhật min, ví dụ màn hình có kích thước 480×320 (3:2) chẳng hạn, thì chúng ta sẽ tính toán để tìm kích thước cũng có tỉ lệ 3:2 và nằm giữa vùng min và max. Ví dụ một trường hợp thoả mãn là 800×534, có tỉ lệ 3:2, và nằm giữa 2 vùng min, max.

Vùng màu xanh lá cây thể hiện cho độ phân giải 800 x 534 ( có tỉ lệ khớp với tỉ lệ của kích thước màn hình 480 x 320)

Và trên thiết bị có kích thước màn hình 480 x 320 sẽ trông như thế này

NHỮNG THÀNH PHẦN ĐỘNG

Một số thành phần trong game như buttons, nếu ta cố định vị trí của chúng trong khi viewport ảo thay đổi theo kích thước màn hình như tính toán ở trên, thì không ổn lắm, vì vậy chúng ta phải làm cho chúng có thể di chuyển được, ví dụ như trong 2 hình dưới đây, mặc dù chạy trên 2 thiết bị có kích thước khác nhau (dẫn đến viewport ảo- hình chữ nhật màu xanh thay đổi), nhưng các buttons luôn di chuyển để nằm ở mép của màn hình:

Các buttons của menu screen trên màn hình có kích thước 854 x 480

Các buttons của menu screen trên màn hình có kích thước 800 x 600

Cuối cùng, bạn hãy xem video mà chúng tôi đã làm để xử lí vấn đề game chạy trên nhiều kích thước này:

một vài hạn chế

Khi chúng ta phóng to/ thu nhỏ hình ảnh trong game để khớp vớ kích thước màn hình, thì trên một vài thiết bị game của chúng ta sẽ bị mờ, và toạ độ vị trí cuối cùng của các thành phần động cũng không còn là só nguyên. Vấn đề này có thể tránh được trên các thiết bị có độ phân giải lớn, cũng như hình ảnh game rõ nét.

Cách bố trí giữa các kích thước màn hình khác nhau cũng khác nhau, ví dụ bố trí game trên điện thoại sẽ khác so với trên máy tính bảng.

Text là một trường hợp đặc biệt, nó có thể bị thu nhỏ đến mức không thể đọc được. Vì vậy trên các thiết bị có độ phân giải thấp, có thể bạn sẽ phải phóng to nó và làm nó dễ đọc hơn.

kết luận

Nếu bạn định làm game theo hướng tiếp cận này của chúng tôi, thì sẽ không khó để làm cho game có thể chạy trên đa màn hình ( theo cách có thể chấp nhận được). Tuy nhiên vẫn còn nhiều chi tiết cần để tâm tới, như chúng tôi đã liệt kê ở trên

Trong phần tiếp theo, chúng tôi sẽ giới thiệu code trên libGDX cho những ai muốn biết chúng tôi thực thi giải pháp này như thế nào

Cảm ơn đã đọc, hi vọng các bạn thích bài viết này.


Mình xin giải thích thêm về Viewport trong libGDX, cho các bạn dễ hình dung hơn:

ex

+ Các hình khối màu cam tượng trưng cho hình ảnh trong game
+ Hình xanh lá cây tượng trưng cho viewport ( theo như kĩ thuật trong bài này thì cái kích thước của viewport này sẽ thay đổi tuỳ vào kích thước màn hình)
+ Hình xanh biển và hồng tượng trưng cho 2 màn hình với 2 kích thước khác nhau

Khi chạy game trên thiết bị, thì viewport xanh lá cây ( cũng như hình ảnh game) sẽ thu nhỏ lại ( đối với xanh biển) hoặc phóng to ra ( đối với hồng) để có thể “áp” vào 2 màn hình kia. Và điểm quan trọng ở đây là tỉ lệ của viewport sẽ bằng với tỉ lệ kích thước màn hình tương ứng

GDPs – II.2 – Mẫu Command

Mẫu Command

Command là một trong những mẫu thiết kế yêu thích của tôi. Đa số chương trình tôi viết, game cũng như 1 số thứ khác, đều có sử dụng mẫu này. Khi sử dụng nó đúng lúc thì những đoạn code rối rắm thực sự trở nên đơn giản hơn nhiều. Gang of Four (GoF – nd: google “Design Patterns by Gang of Four để biết thêm chi tiết) đã mô tả về mẫu này một cách thâm thúy như sau:

Đóng gói một yêu cầu như là một đối tượng (nd: object), từ đó để cho người dùng (nd: lập trình viên) thông số hóa khách hàng với những yêu cầu khác nhau, như xếp hàng hoặc ghi chú các yêu cầu, và hỗ trợ những hành động có thể làm lại.

Tôi nghĩ chúng ta đều đồng ý rằng đây là một câu khủng khiếp. Đầu tiên, nó diễn tả sai những điều nó đang cố truyền tải cho chúng ta . Bên ngoài thế giới thực, nơi mà một từ có thể mang nhiều nghĩa , thì một ” khách hàng ” là một người — ai đó mà bạn kinh doanh cùng. Theo như lần cuối tôi kiểm tra, thì con người không thể được “ tham số hóa ”.

Tiếp đến, phần còn lại của câu liệt kê một loạt các thứ bạn có thể dùng mẫu thiết kế này. Nếu bạn chưa làm bất cứ thứ gì trong danh sách đó thì ứng dụng của mẫu này không rõ ràng lắm đối với bạn. Với tôi thì tôi định nghĩa mẫu Command một cách xúc tích như sau:

Một command (nd: lệnh – mình để command luôn) là một phương thức được sự vật hóa.

Bài dịch Game Design Pattern – Mục lục

Game Design Patter- Robert Nystrom
Bản dịch tiếng việt


MỤC LỤC

i. Lời cảm ơn

  • I. Giới thiệu
    1. Cấu trúc, hiệu năng và games
  • II. Xem lại các mẫu thiết kế
    2. Mẫu Command
    3. Mẫu Flyweight
    4. Mẫu Observer
    5. Mẫu Prototype
    6. Mẫu Singleton
    7. Mẫu State
  • III. Sequencing Patterns
    8. Mẫu Douple Buffer
    9. Mẫu Game Loop
    10. Mẫu Update Method
  • IV. Behavioral Patterns
    11.Bytecode
    12. Subclass Sandbox
    13. Type Object
  • V. Decoupling Patterns
    14. Component
    15. Event Queue
    16. Service Locator
  • VI. Optimization Patterns
    17. Dirty Flag
    18. Object Pool
    19. Spatial Partition
    20. Data Locality