Game Studio
Liên kế mạng xã hội

Game Studio


Hướng dẫn cách tạo hiệu ứng Grayscale cho Sprite trong Cocos 2D-X

Giới thiệu

Hiệu ứng là một trong những phần không thể thiếu được của các game hiện nay. Trong Sins – The Salvation, chúng tôi đã tạo rất nhiều hiệu ứng đẹp mắt. Một trong những hiệu ứng đó là hiệu ứng grayscale. Chúng tôi dùng hiệu ứng này để hiển thị những mode chơi chưa được kích hoạt. Trong bài viết này, tôi sẽ hướng dẫn các bạn cách tạo hiệu ứng này.

Tiền đề bài viết

Bài viết thuộc loạt bài viết chia sẻ các công nghệ sử dụng trong Sins – The Salvation mà nhiều học viên của STDIO và một số thành viên khác đã yêu cầu với chúng tôi.

Đối tượng hướng đến

Tất cả những lập trình viên sử dụng game engine dựa trên nền tảng OpenGLES 2.0 mong muốn tạo nhiều hiệu ứng đẹp cho game.

Phân tích

Ảnh grayscale, hay còn gọi là ảnh trắng đen, là ảnh chỉ có 1 kênh màu duy nhất. Mỗi pixel là một mẩu đơn giản, chứa thông tin cường độ của pixel đó. Giá trị lớn nhất tương ứng với pixel màu trắng và giá trị nhỏ nhất tương ứng với pixel màu đen. Cường độ này có giá trị thường được chọn trong khoảng từ 0 đến 1 hoặc từ 0 đến 255.

Theo nghiên cứu, mắt người nhạy cảm nhất với màu xanh lá cây và ít nhạy cảm với màu xanh dương nên ta có thể áp dụng công thức sau đây để tính toán cường độ xám của ảnh grayscale.

  1. GrayFactor = 0.2126*R + 0.7152*G + 0.0722*B

Với R, G, B lần lượt là cường độ của các kênh màu đỏ, xanh lá cây, xanh dương.

Hiện thực

Sau khi kết thúc bài viết này, các bạn có thể tạo được hiệu ứng có dạng như sau:

Vertex shader tôi giữ giống như shader của Cocos2d-x Vì grayscale sprite chỉ khác sprite thông thường trong Cocos2d-x ở giai đoạn tính toán màu cho từng fragment nên tôi sẽ tận dụng những phần có sẵn của Cocos2d-x và viết thêm fragment shader. Để hoàn thành bài viết này, tôi chỉ cần sử dụng hình ảnh HelloWorld.png được đính kèm trong dự án mẫu của Cocos2d-x.

  1. attribute vec4 a_position;
  2. attribute vec2 a_texCoord;
  3. attribute vec4 a_color;
  4.  
  5. #ifdef GL_ES
  6. varying lowp vec4 v_fragmentColor;
  7. varying mediump vec2 v_texCoord;
  8. #else
  9. varying vec4 v_fragmentColor;
  10. varying vec2 v_texCoord;
  11. #endif
  12. void main()
  13. {
  14. gl_Position = CC_MVPMatrix * a_position;
  15. v_fragmentColor = a_color;
  16. v_texCoord = a_texCoord;
  17. }

Fragment shader của tôi như sau:

  1. #ifdef GL_ES
  2. precision lowp float;
  3. #endif
  4.  
  5. varying vec4 v_fragmentColor;
  6. varying vec2 v_texCoord;
  7. uniform sampler2D u_texture;
  8.  
  9. void main()
  10. {
  11. vec4 texColor = texture2D(u_texture, v_texCoord);
  12. float grayscaleFactor = 0.2126*texColor.r + 0.7152*texColor.g + 0.0722*texColor.b;
  13. vec4 finalColor = vec4(grayscaleFactor, grayscaleFactor,
  14. grayscaleFactor, texColor.a);
  15.  
  16. gl_FragColor = v_fragmentColor * finalColor;
  17. }

Điểm mấu chốt của shader này nằm ở dòng 12. Tại dòng này, tôi đã áp dụng công thức đã được nghiên cứu mà tôi đã nêu ở trên để tính toán cường độ xám của pixel màu. Tại dòng 13, tôi chọn cường độ xám trên làm giá trị cho các kênh màu đỏ, xanh lá cây, xanh dương trong màu được tạo ra.

Sau khi đã viết shader cho grayscale sprite, tôi bắt phần hiện thực component GrayScaleSprite. Đây là phần khởi tạo component của tôi:

  1. bool GrayScaleSprite::init(const char *textureName)
  2. {
  3. if(!CCSprite::initWithFile(textureName))
  4. {
  5. return false;
  6. }
  7.  
  8. CCGLProgram* pProgram = new CCGLProgram();
  9. pProgram->initWithVertexShaderFilename("GrayScaleSprite.vert",
  10. "GrayScaleSprite.frag");
  11.  
  12. setShaderProgram(pProgram);
  13.  
  14. pProgram->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
  15. pProgram->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);
  16. pProgram->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
  17.  
  18. pProgram->link();
  19. pProgram->updateUniforms();
  20.  
  21. pProgram->release();
  22.  
  23. return true;
  24. }

Ở đây, tôi dùng lại hàm khởi tạo với texture của CCSprite và viết thêm phần khởi tạo shader cho component.

Phần render của component hoàn toàn giống với của CCSprite nên tôi không cần viết lại, Cocos2d-x sẽ tự liên kết component của tôi với shader tôi đã viết thêm.

Mã nguồn đầy đủ của component các bạn có thể xem tại đây:

Cách dùng Component

  1. CCSize winSize = CCDirector::sharedDirector()->getWinSize();
  2.  
  3. GrayScaleSprite* pSprite = GrayScaleSprite::create("HelloWorld.png");
  4. pSprite->setPosition(ccp(winSize.width/2.0f, winSize.height/2.0f));
  5. this->addChild(pSprite);

Tổng kết

Đây là một trong những hiệu ứng đơn giản chúng tôi đã áp dụng vào Sins – The Salvation. Hy vọng với bài viết này, các bạn có thể tạo ra được những game phong phú hơn cho riêng mình.

Theo: Stdio


Đăng sự kiện cho developer