Cứ như một giấc mơ
- 7/12/2014 – viết cho 5/12
Quả thật đến bây giờ tôi vẫn nghĩ đó như là một giấc mơ, vì tôi không tin được là em lại có cảm tình với tôi, dù chỉ là một chút. Bỏ qua những suy nghĩ khác về công việc bạn bè mà mọi thứ, chỉ có những suy nghĩ, những tình cảm về em, tôi muốn lưu giữ nó lại.
Đến đón em đi chơi, tôi xem như đây là lần cuối tôi và em gần nhau, tôi không suy nghĩ nhiều về chuyện đó. Vì trong tôi, đã xem em như một ký ức đẹp.
Lúc em nhắn tin nhờ tôi đón đi chơi, tôi chỉ nghĩ em muốn đi cho vui thôi, vì em là một cô bé ham vui mà :D. Vi vu trên đường, không để ý nên bị hố mất một đoạn, trễ hẹn với em mấy chục phút J. Thế mà lúc đến vẫn không thấy em tỏ ra khó chịu gì cả, nhìn thấy em, một chút ký ức trong tôi lại trỗi dậy, tôi muốn cho em biết những tình cảm của mình, nhưng tôi không dám.
Chúng tôi lại im lặng, sự im lặng quen thuộc mỗi lần em ngồi sau tôi…
Đến nhà hàng, có lẽ đây cũng là lần cuối tôi có mặt ở đây, được gặp lại một số gương mặt thân quen, đã bên tôi suốt 4 tháng. Tôi và em tách ra, ngồi nói chuyện phiếm với mấy đứa, lâu lâu lại nhìn em cái, thấy em vẫn vui vẻ là được rồi.
Sau một hồi rủ rê lôi kéo thì mọi người cũng rủ nhau đi nhậu, để xem hôm đó có ai nào: Vân, Xuân, Đức, Sơn, Bảo, Hùng, Hiệp, Kiều, Vân (đen), Loan (móm), em và tôi. Mọi người cạn lên cạn xuống, nhưng nhìn chung thì chỉ có mấy đứa là chịu uống, còn những người khác chỉ nhấp môi. Liếc qua thấy ly bia trước mặt em, tôi không vui chút nào, em bị đau dạ dày mà... Nhưng tôi không có lý do gì để ngăn em, tôi chỉ là một người bạn thôi mà…
Tôi uống, tôi muốn quên đi những cảm xúc này, cạn, cạn và cạn. Một lúc thì hết thùng bia, mấy đứa lại rủ nhau đi kara, nhưng có lẽ sáng mai phải làm nên có mấy người bỏ về. Em muốn đi, tôi cũng đoán được, tôi cũng mong điều đó xảy ra, muốn được gần em thêm chút nữa.
Em lại ngồi phía sau tôi, trời Đà Nẵng mấy hôm nay dần trở lạnh, tôi cảm nhận được chút hơi ấm từ em, điều mà tôi vẫn luôn mong chờ, nhưng tại sao nó lại đến muộn vậy?
Hôm nay không phải lần đầu tôi chở em đi, nhưng những hôm đó tôi chỉ cảm nhận được cái lạnh ngoài kia. Em vẫn luôn hỏi tôi sao tôi không mang áo khoác gì mà để lạnh vậy? Tôi chỉ cười và bảo anh lười đấy, em im lặng... Những hôm thức dậy sớm, chạy gần chục cây số qua đón em đi làm, tay anh lúc nào cũng run cầm cập, rồi những đêm chở em về nữa. Anh lạnh lắm, cái lạnh giá kia với sự cô đơn trong tim như muốn làm anh từ bỏ, từ bỏ đoạn tình cảm này...
Nhiều lúc tự hỏi làm thế này có đáng không, có cần phải làm vậy không?? Những lúc đó tôi chỉ cười mỉm mà nghĩ rằng, thôi không quan tâm nữa, chỉ vì trong tim tôi đã có hình bóng em, vậy là đủ rồi. Một chút suy nghĩ về em, một chút vẩn vơ làm tim tôi cảm thấy ấm áp để tiếp tục giữ lại đoạn tình cảm này. Đối với tôi thì tình yêu là một thứ gì đó xa vời, nên dù rằng đây chỉ là một chút tình cảm từ một phía, tôi vẫn muốn giữ mãi nó trong tim mình. Đúng là tự mình đa tình mà, hì.
Một chút ký ức đó để tôi quay về với thực tại, em đang ngồi sát phía sau. Tôi có thể cảm nhận được hơi ấm từ cơ thể em. Bất giác tôi quay lại, điều này như một thói quen khi chở em, tôi muốn xem em còn ngồi đó không hay rơi đâu mất rồi =)), có chuyện này là bởi bình thường em thường ngồi cách tôi một quãng, dường như quá xa để tôi không cảm nhận được sự tồn tại của em. Lúc này đây tôi quay lại, nhìn thấy rõ gương mặt em ngay bên mình, lúc này em đang cười mỉm, tôi không hiểu lắm, lại tiếp tục lái xe đi.
Bất chợt em áp sát vào và hỏi tôi:
- Anh có gì muốn nói với em không? - ngay lúc đó tôi thấy đầu óc mình như trống rỗng, tôi hỏi lại theo bản năng - nói gì hả em?
- Thì có chuyện gì muốn nói với em không? - em lại nói, hình như có đôi chút ngại ngùng. Tôi im lặng mất một lúc để tiêu hóa một đống suy nghĩ vừa lóe lên trong đầu.
- Em không cảm nhận được chút nào sao? - tôi hỏi lại em sau một lúc cân nhắc, giọng tôi có chút run rẩy.
Lần này đến lượt em im lặng. Chúng tôi im lặng cho đến lúc đến nơi, quán đóng cửa, cả bọn giải tán. Sau một hồi tranh cãi giữa Đức với Sơn thì tôi vẫn là người chở em về.
Lần này vẫn im lặng, nhưng tôi có cảm giác điều gì đó sắp xảy ra, trái tim của tôi có lẽ đang lỗi nhịp.
- Anh nói tiếp đi - em lại mở lời, không biết em muốn nghe điều gì đây...
- Em có người yêu rồi đúng không?
- Nếu em nói chưa có, anh nghĩ sao?
- Không tin chứ sao. - hình như tôi hơi phũ =)), em im lặng một lúc lại hỏi tiếp:
- Tại sao anh lại biết?
- Anh lùng sục fb của em nhiều mà ~.~ - tôi còn nhớ lúc đó có cảm giác hơi ngại, giống như mình làm sai chuyện gì vậy. Em thì im lặng, không biết đang nghĩ gì...
Có lẽ tôi muốn phá tan cái không khí im lặng này, tôi không muốn lần gặp cuối cùng này của tôi với em kết thúc như vậy áy lúc tôi đang cố điều chỉnh lại tâm trạng thì em lại hỏi tôi:
- Tại sao anh lại có cảm tình với em chứ không phải một cô gái khác? - em nói nhanh và rành mạch, giống như nó đã được chuẩn bị từ lâu rồi vậy...
- Em còn nhớ lúc nhà hàng tổ chức cho nhân viên đi chơi không?
- Ừm
- Lần đó anh có nói là anh thích giọng nói của em. - tôi vẫn nhớ lần đó em ngồi sau xe tôi líu lo cả buổi.
- Tại sao anh lại thích?
- Anh thấy nó ngọt ngào mà nhẹ nhàng.
- Thế anh chỉ thích giọng nói của em thôi à? - hình như em đang cố ý đùa tôi.
- Anh không biết, nhưng anh thấy thích con người em từ lúc mới gặp, anh thấy em đáng yêu, có chút con nít...
- Khi nào anh trở lại Đà Nẵng?
- Anh không biết, nếu đợt này về không trúng nghĩa vụ thì ra tết có khi anh vào lại, còn nếu trúng thì 2 năm, anh cũng chịu.
Em không nói gì, cũng sắp đến nhà em rồi, em chỉ bảo tôi dừng lại trước kiệt nhà em. Hình như em không muốn cho người nhà biết tôi chở em về thì phải :(. Tôi dừng xe tại chỗ mà tôi vẫn thường để em lại, cách nhà em chừng 50m. Em xuống xe, ngay lúc tôi tưởng em sẽ nói lời tạm biệt thì em lại làm tôi ngơ ra tập 2:
- Anh còn gì muốn nói với em nữa không? - Em lúc này đang cầm cái mũ bảo hiểm đưa qua đưa lại, có lẽ đang bối rối đây. Còn tôi, đang run lên ấy chứ, nói hay không nói? Nói ra thì cũng không có kết quả, mà không nói thì sau này lại tiếc, nghĩ lên nghĩ xuống, và có lẽ tình cảm dồn nén thời gian qua, tôi đã bày tỏ tình cảm của mình với em:
- Anh thích em. - tôi nói rõ ràng từng chữ và đợi phản ứng của em, sau khi nghe xong câu đó, tôi thấy em mỉm cười, em hơi cúi mặt xuống, có lẽ để che giấu cái gì đó ^^. Em hỏi:
- Đây là lần đầu anh nói điều này với một người con gái à?
- Ừa - em lại im lặng, tôi không nghĩ em lại đoán được, đây đúng là lần đầu tiên tôi bày tỏ tình cảm của mình, nói được câu "anh thích em" với một cô gái.
- Tại sao anh không nói sớm hơn?
- Em có nhớ lần anh dẫn em với anh V đi ăn không?
- Có anh.
- Lần đó anh phát hiện em có người yêu. – tôi nói có phần ấp úng vì có chuyện không đẹp lắm sắp được tôi kể ra.
- Anh nhìn điện thoại em à? - tôi lại bất ngờ thêm lần nữa, nhớ lại về buổi đi chơi đó. Chính em rủ tôi đi, cũng bảo đi 2 đứa, tôi cứ nghĩ hôm đó sẽ làm quan hệ của chúng tôi tiến thêm bước nữa. Mặc dù có lòi thêm 1 người nhưng tôi vẫn không quan tâm lắm, lần đó vô tình tôi lướt qua màn hình điện thoại em, hình như em đang nhắn tin với ai đó. Còn nhớ lúc đó tim tôi như bị ai đó nện cho một cái vậy, đau lắm...
Một loạt tin nhắn đến, chỉ cần nhìn qua là tôi có thể đoán được rồi, ở giữ đống ký tự gì đó là 2 chữ mà chỉ cần nó đã nói lên tất cả. Tôi đơ người mất gần 2s, sau đó quay mặt đi, em mỉm cười cất điện thoại đi và có nhìn lướt qua tôi, sau này nghĩ lại tôi cảm thấy đoạn này cứ như em cố ý cho tôi thấy vậy. Trước kia cũng có lần tôi cầm điện thoại em rồi, nhưng cũng chả tò mò gì. Bao nhiêu suy nghĩ lóe lên trong đầu tôi, thất vọng cũng có, buồn cũng có, tiếc nuối,... Nhưng tôi cố gắng để không biểu hiện gì ra ngoài, chúng tôi kết thúc một buổi đi chơi vui vẻ.
Tối hôm đó tôi suy nghĩ rất nhiều, để rồi đưa ra một quyết định, mặc dù tôi cảm thấy nó có chút “ngớ ngẩn”, nhưng tôi vẫn quyết vậy, đó là tránh tiếp xúc với em. Khoảng thời gian tháng 11, tôi ít nói chuyện với em hơn, không như hồi trước mỗi khi em ngang qua tôi thường đùa 1 2 câu. Tuy thế nhưng tình cảm trong tôi nó vẫn không mất đi, hằng ngày gặp em đó, giúp đỡ khi em cần, thấy em vẫn vui vẻ, dần trong tôi không còn nghĩ về những chuyện buồn đó nữa. Tôi chỉ đơn giản nghĩ em là một đoạn ký ức, một đoạn tình cảm trong đời tôi, vậy là đủ rồi.
Quay lại lúc đó, khi em hỏi tôi câu đó, ngay lập tức tôi nghĩ chuyện lúc trước là em cố ý, em muốn tôi từ bỏ. Nhưng rồi tôi cũng không chắc chắn, dù sao tôi vẫn thấy sợ trực giác của phụ nữ =)). Tôi trả lời em một cách ngập ngừng:
- Lúc đó anh vô tình thấy trong điện thoại em, trong tin nhắn có chữ Ck, là anh biết rồi. – nói xong tôi cố tránh ánh mắt của em. Em cười, có lẽ em thấy tôi ngớ ngẩn quá thì phải ~.~
** mất một đoạn, vì đoạn sau làm tôi khá là bất ngờ, kể cả những việc sau đây tôi cũng không nhớ rõ thứ tự nữa :D**
- Anh làm em cảm thấy có lỗi quá... – em làm tôi khá bất ngờ.
- Em có lỗi gì, chỉ tại anh không biết tận dụng thời cơ thôi... – lại im lặng, lúc này tôi thực sự ghét nó, trong thôi như có cái gì đó thôi thúc vậy, nó cứ thế dâng lên làm tôi không thể khống chế được, tôi nói với em một câu mà mình cũng không ngờ đến:
- Cho anh ôm cái nha. – em cúi đầu bước đến với tiếng “ừm” nhỏ. Tôi vòng tay ôm em sát vào người mình, cứ như một giấc mơ vậy, tôi ước gì nó là vĩnh hằng, là mãi mãi. Cảm nhận được hơi ấm từ em, cảm nhận được vòng tay của em đang ôm tôi, tôi vui lắm. Tôi là người chủ động bỏ em ra, mặc dù tôi muốn ôm em nữa ^^.
- Em có suy nghĩ gì về anh không? – tôi hỏi mà có chút mong chờ.
- Hơn bạn một chút... – câu trả lời của em làm tôi khá buồn, hơn bạn, chung quy vẫn là bạn...nhưng mà điều em đã làm cho tôi như vậy là đủ rồi, không nên đòi hỏi quá nhiều.
Tôi nhìn em thật kỹ, như muốn ghi nhớ những giây phút cuối cùng này, hình bóng em vào sâu trong tim.
- Tạm biệt em nhé.
- Cũng gần 12h rồi, thôi anh về đi. – em nhìn thoáng qua đồng hồ nói. - Em vào trước đi. – Em thoáng nhìn tôi rồi bước đi, tôi cũng không nhớ mình có chờ em đi khuất chưa, cũng chả nhớ mình về phòng thế nào... Tôi không nhớ, không muốn nhớ gì nữa,.. liệu chuyện này đến đây có phải kết thúc rồi không?
Wednesday, December 17, 2014
Tuesday, May 6, 2014
Lấy tất cả Resource trong drawable của Android
Đoạn mã sau cung cấp cách để lấy ra tất cả resource trong drawable của android.
final R.drawable drawableResources = new R.drawable();
final Class<R.drawable> c = R.drawable.class;
final Field[] fields = c.getDeclaredFields();
for (int i = 0, max = fields.length; i < max; i++) {
final int resourceId;
try {
resourceId = fields[i].getInt(drawableResources);
} catch (Exception e) {
continue;
}
/* make use of resourceId for accessing Drawables here */
}
Wednesday, April 23, 2014
Đặc điểm của Vector, ArrayList trong Java
![]() |
ArrayList và Vector trong java |
Các lớp Vector và ArrayList được triển khai xây dựng dựa trên mảng Object[] dùng để lưu trữ các phần tử. Việc truy xuất đến các phần tử cũng đơn giản thông qua việc truy xuất theo chỉ mục(index) như trong mảng.
Đặc điểm của Vector và ArrayList là chúng ta có thể thêm những phần tử mới vào mà không gặp trở ngại về “sức chứa” như Array thông thường là phải định rõ độ lớn(chiều dài) của mảng.
Vậy, chúng “giãn nở” như thế nào ? Như đã nói ở trên Vector & ArrayList được xây dựng dựa trên mảng các Object[]. Mảng này có thể lớn hơn độ lớn các phần tử cấu thành lên Vector/ArrayList object nhằm giữ khả năng có thể thêm phần tử mới khi cần. Khi tận dụng hết khả năng sức chứa của mảng này thì Vector/ArrayList object phải tạo ra một mảng mới có độ lớn (sức chứa) hơn mảng cũ và những phần tử trước đó sẽ được sao chép chuyển sang mảng mới này. Về độ lớn của mảng mới tạo ra so với mảng cũ sẽ tùy thuộc vào các version SDK mà dao động trong khoảng 50 – 100 (%). 2 đoạn code dưới đây là một minh chứng :
public void ensureCapacity(int minCapacity) { int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3) / 2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); } } public void ensureCapacity(int minCapacity) { int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = Math.max(oldCapacity * 2, minCapacity); elementData = new Object[newCapacity]; System.arrayscopy(oldData, 0, elementData, 0, size); } }
Tính hiệu quả khi chèn phần tử mới vào Vector & ArrayList :
- Chèn vào vị trí index = 0 sẽ cho tính hiệu quả kém nhất (the worst performance) vì tất cả những phần tử trước đó sẽ phải dịch chuyển lên trên.
- Chèn vào vị trí sau phần tử đứng cuối cùng sẽ cho hiệu quả tốt nhât (the best performance) vì các phần tử trước đó vẫn giữ nguyên vị trí không có một sự xáo trộn nào xảy ra.
- Từ đây ta cũng có thể hiển được tính hiệu quả khi chèn vào vị trí bất kì nào khác 2 vị trí trên.
Điểm khác biệt chính giữa Vector và ArrayList là sử dụng cơ chế đồng bộ (synchronization). Ngoài 2 methods sử dụng serialize thì ArrayList không có một methods nào được đồng bộ hóa. Ngược lại với Vector, hầu hết các phương thức (method) của Vector được đồng bộ hóa. Vì thế Vector là thread-safe còn ArrayList thì không. Trong những ứng dụng đa luồng thì việc sử dụng Vector là hợp lý hơn vì tính đồng bộ của nó thích hợp cho việc giải quyết nhiều tương tác trong một thời điểm. Còn các phương thức của ArrayList sẽ cho tốc độ xử lý nhanh hơn trong trường hợp đơn giản không đòi hỏi xử lý nhiều tương tác trong một thời điểm. Sự cách biệt về tốc độ xử lý giữa 2 class này càng ngày càng được thu hẹp lại với những version JVMs (máy ảo Java) mới nhất.
(Sưu tầm)
Saturday, March 15, 2014
OpenGL ES 2.0 trên android - Trả lời sự kiện chạm
Trả lời sự kiện chạm
Làm cho đối tượng di chuyển theo một chương trình định sẵn như hình tam giác xoay rất hữu ích để có được một số sự chú ý, nhưng những gì nếu bạn muốn có người dùng tương tác với đồ họa OpenGL ES của bạn? Chìa khóa để làm ứng dụng cảm ứng OpenGL ES của bạn tương tác được mở rộng thực hiện của bạn GLSurfaceView để ghi đè lên onTouchEvent () để lắng nghe cho các sự kiện liên lạc.
Bài học này cho bạn thấy làm thế nào để lắng nghe cho các sự kiện liên lạc cho phép người dùng xoay một đối tượng OpenGL ES.
1.1. Cài đặt một Touch Listener
Để làm cho ứng dụng OpenGL ES phản ứng với các sự kiện chạm vào, bạn phải thực hiện onTouchEvent() trong lớp GLSurfaceView của bạn. Việc thực hiện ví dụ dưới đây cho thấy làm thế nào để lắng nghe cho các sự kiện MotionEvent.ACTION_MOVE và dịch chúng vào một góc quay cho một shape.
@Override public boolean onTouchEvent(MotionEvent e) { // MotionEvent reports input details from the touch screen // and other input controls. In this case, you are only // interested in events where the touch position changed. float x = e.getX(); float y = e.getY(); switch (e.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - mPreviousX; float dy = y - mPreviousY; // reverse direction of rotation above the mid-line if (y > getHeight() / 2) { dx = dx * -1 ; } // reverse direction of rotation to left of the mid-line if (x < getWidth() / 2) { dy = dy * -1 ; } mRenderer.setAngle( mRenderer.getAngle() + ((dx + dy) * TOUCH_SCALE_FACTOR); // = 180.0f / 320 requestRender(); } mPreviousX = x; mPreviousY = y; return true; }
Chú ý rằng sau khi tính toán góc quay, phương pháp này gọi requestRender () để cho các renderer rằng đó là thời gian để làm cho khung. Phương pháp này là hiệu quả nhất trong ví dụ này vì khung không cần phải được vẽ lại, trừ khi có một sự thay đổi trong vòng quay. Tuy nhiên, nó không có bất kỳ tác động hiệu quả trừ khi bạn cũng yêu cầu các renderer chỉ vẽ lại khi dữ liệu thay đổi bằng cách sử dụng hàm setRenderMode(), do đó hãy chắc chắn rằng dòng này là không chú thích trong renderer:
public MyGLSurfaceView(Context context) { ... // Render the view only when there is a change in the drawing data setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); }
1.2. Phơi bày các góc quay
Mã ví dụ trên yêu cầu bạn tiếp xúc với các góc quay thông qua renderer của bạn bằng cách thêm một thành viên public. Kể từ khi mã renderer đang chạy trên một thread khác từ phía giao diện người dùng chính thread của ứng dụng của bạn, bạn phải khai báo biến công cộng này là volatile. Đây là mã để làm điều đó:
public class MyGLRenderer implements GLSurfaceView.Renderer { ... public volatile float mAngle;
1.3. Áp dụng phép xoay
Áp dụng phép xoay được tạo ra bởi đầu vào cảm ứng, chú thích ra các mã để tạo một góc và thêm mAngle, trong đó có các đầu vào tạo ra góc cảm ứng:
public void onDrawFrame(GL10 gl) { ... float[] scratch = new float[16]; // Create a rotation for the triangle // long time = SystemClock.uptimeMillis() % 4000L; // float angle = 0.090f * ((int) time); Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f); // Combine the rotation matrix with the projection and camera view // Note that the mMVPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // Draw triangle mTriangle.draw(scratch); }
Khi bạn đã hoàn thành các bước mô tả ở trên, chạy chương trình và kéo tay của bạn trên màn hình để xoay hình tam giác:
Figure 1. Triangle being rotated with touch input (circle shows touch location).
Nhãn:
android,
it,
opengl es,
opengl es 2.0,
touch listener
OpenGL ES 2.0 trên android - Thêm chuyển động
Thêm chuyển động
Vẽ các đối tượng trên màn hình là một tính năng khá cơ bản của OpenGL, nhưng bạn có thể làm điều này với các lớp học framwork đồ họa Android khác, bao gồm cả Canvas và các đối tượng drawable. OpenGL ES cung cấp khả năng bổ sung cho việc di chuyển và chuyển đối tượng được vẽ với không gian ba chiều hoặc theo những cách độc đáo khác để tạo ra những trải nghiệm người dùng hấp dẫn.
Trong bài học này, bạn lấy một bước chuyển tiếp vào sử dụng OpenGL ES bằng cách học làm thế nào để thêm chuyển động với một shape với phép xoay.
1.1. Xoay một Shape
Quay một đối tượng vẽ với OpenGL ES 2.0 là tương đối đơn giản. Bạn tạo ra một ma trận biến đổi (một ma trận xoay) và sau đó kết hợp nó với chiếu của bạn với camera ma trận biến đổi:
private float[] mRotationMatrix = new float[16]; public void onDrawFrame(GL10 gl) { ... float[] scratch = new float[16]; // Create a rotation transformation for the triangle long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f); // Combine the rotation matrix with the projection and camera view // Note that the mMVPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // Draw triangle mTriangle.draw(scratch); }
Nếu tam giác của bạn không quay sau khi thực hiện những thay đổi này, chắc chắn rằng bạn đã chú thích thiết lập GLSurfaceView.RENDERMODE_WHEN_DIRTY, như được mô tả trong phần tiếp theo.
1.2. Cho phép Rendering liên tục
Nếu bạn đã tinh tấn làm theo cùng với các mã ví dụ trong lớp này đến thời điểm này, chắc chắn rằng bạn nhận xét ra các dòng thiết lập chế độ làm chỉ vẽ khi thay đổi, nếu không OpenGL chỉ quay một lần và chờ đợi cho một cuộc gọi đến requestRender () từ container GLSurfaceView:
Trừ khi bạn có các đối tượng thay đổi mà không cần bất kỳ tương tác người dùng, nó thường là một ý tưởng tốt có lá cờ này bật. Hãy sẵn sàng để bỏ ghi chú mã này, bởi vì bài học tiếp theo làm cho cuộc gọi này được áp dụng một lần nữa.
OpenGL ES 2.0 trên android - Áp dụng phép chiếu và camera
Áp dụng phép chiếu và camera
Trong môi trường
OpenGL ES, phép chiếu và máy quay cho phép bạn hiển thị các đối tượng hình vẽ
theo cách giống cách bạn nhìn thấy đối tượng vật lý với đôi mắt của bạn. Mô phỏng
vật lý này được thực hiển với biến đối toán học của tọa độ hình vẽ:
·
Chiếu -
chuyển đổi này điều chỉnh tọa độ của đối tượng được vẽ dựa trên chiều rộng và
chiều cao của GLSurfaceView nơi chúng được hiển thị. Mà không cần tính toán
này, các đối tượng được vẽ bởi OpenGL ES đang sai lệch bởi tỷ lệ đồng đều của cửa
sổ xem. Một chuyển đổi chiếu thường chỉ phải được tính toán khi tỷ lệ của khung
nhìn OpenGL được thành lập hoặc thay đổi trong onSurfaceChanged() của renderer
của bạn. Để biết thêm thông tin về OpenGL ES dự và phối hợp ánh xạ, xem bản đồ
tọa độ cho các đối tượng được vẽ.
·
Camera -
chuyển đổi này điều chỉnh tọa độ của đối tượng được vẽ dựa trên một vị trí máy
quay ảo. Điều quan trọng cần lưu ý là OpenGL ES không định nghĩa một đối tượng
máy ảnh thực tế, nhưng thay vì cung cấp phương pháp hữu ích mà mô phỏng một máy
ảnh bằng cách biến đổi màn hình hiển thị của các đối tượng vẽ. Một biến đổi
camera có thể được tính toán một lần khi bạn thiết lập GLSurfaceView của bạn,
hoặc có thể thay đổi tự động dựa trên các hành động sử dụng hay chức năng ứng dụng
của bạn.
Bài học này mô tả làm thế nào để tạo ra một chiếu
và xem máy ảnh và áp dụng nó cho Shapes được vẽ trong GLSurfaceView của bạn.
1.1. Định nghĩa phép chiếu
Dữ liệu cho một biến đổi
của phép chiếu được tính toán trong hàm onSurfaceChanged() của lớp
GLSurfaceView.Renderer. Ví dụ dưới đây lấy chiều cao và rộng của GLSurfaceView
và sử dụng để chứa biến đổi ma trận phép chiếu sử dụng hàm Matrix.frustumM().
@Override public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; // this projection matrix is applied to object coordinates // in the onDrawFrame() method Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); }
Mã này chứa một ma trận
chiếu, mProjectionMatrix mà bạn có thể kết hợp với sự chuyển đổi camera quan
sát trong hàm onDrawFrame(), được thể hiện trong phần tiếp theo.
Lưu ý: Chỉ cần áp dụng
một phép chiếu cho đối tượng vẽ của bạn, nếu không kết quả sẽ là một màn hình
trống rỗng. Nói chung, bạn cũng phải áp dụng các chuyển camera để cho bất cứ điều
gì để hiển thị trên màn hình.
1.2. Định nghĩa một Camera
Hoàn tất quá trình
chuyển đối tượng vẽ của bạn bằng cách thêm một biến đổi camera như một phần của
quá trình vẽ. Trong mã ví dụ sau đây, việc chuyển đổi camera được tính toán bằng
cách sử dụng Matrix.setLookAtM () và sau đó kết hợp với ma trận chiếu tính trước
đó. Các ma trận biến đổi kết hợp này sau đó được thông qua để vẽ ra shape.
@Override public void onDrawFrame(GL10 unused) { ... // Set the camera position (View matrix) Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // Calculate the projection and view transformation Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); // Draw shape mTriangle.draw(mMVPMatrix); }
1.3. Áp dụng phép chiếu và camera
Để sử dụng chiếu kết hợp
với camera ma trận biến đổi thể hiện trong phần xem trước, chỉnh sửa hàm draw
() của đối tượng đồ họa của bạn để chấp nhận các ma trận biến đổi kết hợp và áp
dụng nó vào shape:
public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix ... // get handle to shape's transformation matrix mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); // Pass the projection and view transformation to the shader GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); ... }
Một khi bạn đã tính
toán một cách chính xác và áp dụng phép chiếu với camera chuyển đổi, các đối tượng
đồ họa của bạn được rút ra theo tỷ lệ chính xác và sẽ giống như thế này:
Figure 1. Triangle drawn
with a projection and camera view applied.
Bây giờ bạn có một ứng
dụng hiển thị shape của bạn với tỷ lệ chính xác, đã đến lúc để thêm chuyển động
cho hình của bạn.
Nhãn:
android,
camera,
it,
opengl es,
opengl es 2.0,
projection,
shape,
shapes
OpenGL ES 2.0 trên android - Định nghĩa Shapes - Hình
Định nghĩa Shapes
Việc có thể để xác định
Shapes để vẽ trong context của một khung nhìn OpenGL ES là bước đầu tiên trong
việc tạo ra tác phẩm đồ họa cao cấp của bạn. Vẽ với OpenGL ES có thể là một
chút khó khăn mà không biết một số điều cơ bản về cách OpenGL ES hy vọng bạn
xác định đối tượng đồ họa.
Bài học này giải thích
OpenGL ES phối hợp hệ thống liên quan đến một màn hình thiết bị Android, những
điều cơ bản của việc xác định Shapes, bề mặt Shapes, cũng như xác định một tam
giác và một hình vuông.
1.1. Định nghĩa một Tam giác
OpenGL ES cho phép bạn
xác định đối tượng rút ra sử dụng tọa độ trong không gian ba chiều. Vì vậy, trước
khi bạn có thể vẽ một hình tam giác, bạn phải xác định tọa độ của nó. Trong
OpenGL, cách điển hình để làm điều này là để xác định một mảng đỉnh của số thực.
Cho hiệu quả tối đa, bạn viết các tọa độ vào một ByteBuffer, nó sẽ thông qua
vào các đường ống dẫn đồ họa OpenGL ES để xử lý.
public class Triangle { private FloatBuffer vertexBuffer; // number of coordinates per vertex in this array static final int COORDS_PER_VERTEX = 3; static float triangleCoords[] = { // in counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, -0.311004243f, 0.0f, // bottom left 0.5f, -0.311004243f, 0.0f // bottom right }; // Set color with red, green, blue and alpha (opacity) values float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f }; public Triangle() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (number of coordinate values * 4 bytes per float) triangleCoords.length * 4); // use the device hardware's native byte order bb.order(ByteOrder.nativeOrder()); // create a floating point buffer from the ByteBuffer vertexBuffer = bb.asFloatBuffer(); // add the coordinates to the FloatBuffer vertexBuffer.put(triangleCoords); // set the buffer to read the first coordinate vertexBuffer.position(0); } }
Theo mặc định, OpenGL
ES giả định một hệ thống phối hợp nơi [0,0,0] (X, Y, Z) xác định tâm của khung
GLSurfaceView, [1,1,0] là góc trên bên phải của khung và [- 1, -1,0] là góc dưới
bên trái của khung. Cho một minh họa của hệ thống phối hợp này, hãy xem hướng
phát triển OpenGL ES.
Lưu ý rằng các tọa độ
của Shapes này được xác định theo một thứ tự ngược chiều kim đồng hồ. Thứ tự bản
vẽ là quan trọng vì nó xác định mặt trước và mặt sau của bề mặt của Shapes mà bạn
vẽ, với mặt sau bề mặt, bạn có thể chọn không vẽ bằng cách sử dụng tính năng
tiêu hủy mặt OpenGL ES. Để biết thêm thông tin về mặt và hủy, xem hướng dẫn
phát triển OpenGL ES.
1.2. Định nghĩa hình vuông
Xác định tam giác là
khá dễ dàng trong OpenGL, nhưng nếu bạn muốn phức tạp hơn một chút? Gì nhỉ? một
hình vuông? Có một số cách để làm điều này, nhưng một con đường điển hình để vẽ
một hình dạng như vậy trong OpenGL ES là sử dụng hai hình tam giác được vẽ với
nhau:
Một lần nữa, bạn nên
xác định các đỉnh theo một thứ tự ngược chiều kim đồng hồ cho cả hai hình tam
giác mà đại diện cho hình dạng này, và đặt các giá trị trong một ByteBuffer. Để
tránh việc xác định tọa độ hai chia sẻ bởi mỗi tam giác hai lần, sử dụng một
danh sách bản vẽ để cho các đường ống dẫn đồ họa OpenGL ES cách vẽ các đỉnh.
Đây là mã cho hình này:
public class Square { private FloatBuffer vertexBuffer; private ShortBuffer drawListBuffer; // number of coordinates per vertex in this array static final int COORDS_PER_VERTEX = 3; static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left -0.5f, -0.5f, 0.0f, // bottom left 0.5f, -0.5f, 0.0f, // bottom right 0.5f, 0.5f, 0.0f }; // top right private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices public Square() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) squareCoords.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoords); vertexBuffer.position(0); // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); } }
Ví dụ này cung cấp cho
bạn một cái nhìn vào những gì nó cần để tạo ra hình dạng phức tạp hơn với
OpenGL. Nói chung, bạn sử dụng danh sách các hình tam giác để vẽ các đối tượng.
Trong bài học tiếp theo, bạn tìm hiểu làm thế nào để vẽ các hình dạng trên màn
hình.
2. Vẽ Shapes
Sau khi bạn xác định
shapes để được vẽ với OpenGL, bạn có thể muốn vẽ chúng. Vẽ hình với OpenGL ES
2.0 có thêm một chút mã hơn bạn có thể tưởng tượng, bởi vì các API cung cấp rất
nhiều quyền kiểm soát các đường ống dẫn vẽ đồ họa.
Bài học này giải thích
làm thế nào để vẽ các hình dạng bạn định nghĩa trong bài học trước bằng cách sử
dụng OpenGL ES 2.0 API.
2.1. Khởi tạo Shapes
Trước khi bạn vẽ bất kỳ
thứ gì, bạn phải khởi tạo và tải các Shapes mà bạn muốn vẽ. Trừ khi cấu trúc của
các Shapes thay đổi trong quá trình thực hiện, bạn nên khởi tạo chúng trong
onSurfaceCreated () của renderer của bạn cho bộ nhớ và xử lý hiệu quả.
public void onSurfaceCreated(GL10 unused, EGLConfig config) { ... // initialize a triangle mTriangle = new Triangle(); // initialize a square mSquare = new Square(); }
2.2. Vẽ Shape
Vẽ một định nghĩa Shape
bằng cách sử dụng OpenGL ES 2.0 đòi hỏi một số lượng đáng kể của mã, bởi vì bạn
phải cung cấp rất nhiều chi tiết để các đường ống dẫn vẽ đồ họa. Cụ thể, bạn phải
xác định như sau:
·
Vertex
Shader - đồ họa OpenGL ES đang để hiển thị các đỉnh của một Shape.
·
Fragment
Shader – mã OpenGL ES cho dựng hình bề mặt của một shape với màu sắc hoặc Textures.
·
Program -
Một đối tượng OpenGL ES có chứa các bóng đổ bạn muốn sử dụng để vẽ một hoặc nhiều
hình dạng.
Bạn cần ít nhất một
vertex shader để vẽ một shape và một fragment shader để tô màu cho shape. Các
bóng đổ phải được hoàn chỉnh và sau đó thêm vào một chương trình OpenGL ES, sau
đó được sử dụng để vẽ hình dạng. Dưới đây là một ví dụ về cách xác định shaders
cơ bản bạn có thể sử dụng để vẽ một hình dạng:
private final String vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}";
Shaders chứa OpenGL
Shading Language (GLSL) mã phải được biên dịch trước khi sử dụng nó trong môi
trường OpenGL ES. Để biên dịch mã này, tạo ra một utility method trong lớp
renderer của bạn:
public static int loadShader(int type, String shaderCode){ // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; }
Để vẽ shape của bạn, bạn
phải biên dịch shader code, thêm chúng vào một đối tượng program OpenGL ES và
sau đó liên kết các program. Làm điều này trong hàm khởi tạo đối tượng, vì vậy
nó chỉ được thực hiện một lần.
Lưu ý: Biên dịch
shaders OpenGL ES và các liên kết program là tốn kém về chu kỳ CPU và thời gian
xử lý, vì vậy bạn nên tránh làm điều này nhiều hơn một lần. Nếu bạn không biết
nội dung của các bóng đổ của bạn tại thời gian chạy, bạn nên xây dựng mã của bạn
như vậy mà họ chỉ được tạo ra một lần và sau đó lưu lại để sử dụng sau này.
public class Triangle() { ... int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables }
Tại thời điểm này, bạn
đã sẵn sàng để thêm các lời gọi vẽ shape của bạn. Vẽ hình với OpenGL ES yêu cầu
bạn chỉ định một số thông số để cho các đường ống dẫn vẽ những gì bạn muốn vẽ
và làm thế nào để vẽ nó. Kể từ khi lựa chọn vẽ có thể khác nhau bởi hình dạng,
đó là một ý tưởng tốt để có các lớp hình dạng của bạn chứa logic vẽ của mình.
Tạo ra một hàm draw()
để vẽ shape. Mã này thiết đặt vị trí và giá trị màu cho vertex shader và
fragment shader, và sau đó thực hiện chức năng vẽ.
public void draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // get handle to fragment shader's vColor member mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set color for drawing the triangle GLES20.glUniform4fv(mColorHandle, 1, color, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); }
Một khi bạn có tất cả
các mã này, vẽ đối tượng này chỉ đòi hỏi một lời gọi đến draw() từ bên trong
phương pháp onDrawFrame()của renderer của bạn. Khi bạn chạy ứng dụng, nó sẽ giống
như thế này:
Có một vài vấn đề với
mã ví dụ này. Trước hết, nó sẽ không gây ấn tượng với bạn bè của bạn. Thứ hai,
hình tam giác là một chút bị đè nén và thay đổi hình dạng khi bạn thay đổi hướng
màn hình của thiết bị. Lý do hình dạng bị lệch là do thực tế là các đỉnh của đối
tượng đã không được sửa chữa cho tỷ lệ diện tích màn hình, nơi các
GLSurfaceView được hiển thị. Bạn có thể khắc phục vấn đề bằng cách sử dụng chiếu
và xem ảnh trong bài học tiếp theo.
Cuối cùng, hình tam
giác là hình cơ bản, nên có một chút nhàm chán. Trong bài học thêm chuyển độn,
bạn làm cho hình dạng này xoay và sử dụng thú vị hơn của đường ống đồ họa
OpenGL ES.
Subscribe to:
Posts (Atom)