Search…

Vẽ Sprite Sử Dụng Mặt Nạ (Cocos2d-x 2.x)

28/09/20203 min read
Hướng dẫn tạo hiệu ứng vẽ sprite sử dụng mặt nạ cho vòng ma thuật trong Cocos2d-x

Hiệu ứng là 1 trong những phần không thể thiếu được của các game. 

Trong bài viết này sẽ hướng dẫn tạo hiệu ứng vẽ sprite sử dụng mặt nạ cho vòng ma thuật.

Hiện thực

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

ss_1

Để hoàn thành bài viết này, cần sử dụng 2 hình ảnh:

  • Hình ảnh thứ nhất là hình ảnh HelloWorld.png được đính kèm trong dự án mẫu của Cocos2d-x.
  • Hình ảnh thứ 2 là hình ảnh Mask.png được đính kèm bên dưới.
ss_2

Để vẽ được sprite sử dụng mặt nạ, cần phải viết shader hỗ trợ multi-texture. Trong bài viết này sử dụng 2 texture:

  • texture thứ nhất để làm nền.
  • texture thứ hai để làm mặt nạ che texture nền.

Vertex shader giữ giống như shader của Cocos2d-x:

attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif

void main()
{
	gl_Position = CC_MVPMatrix * a_position;
	v_fragmentColor = a_color;
	v_texCoord = a_texCoord;
}

Fragment shader:

#ifdef GL_ES
precision lowp float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform sampler2D u_mask;

void main()
{
	vec4 texColor = texture2D(u_texture, v_texCoord);
	vec4 maskedColor = texture2D(u_mask, v_texCoord);
	vec4 blendingColor = vec4(texColor.r, texColor.g,
				texColor.b, maskedColor.a * texColor.a);

	gl_FragColor = v_fragmentColor * blendingColor;
}

3 kênh màu đỏ, xanh lá cây, xanh dương của màu cuối cùng được tạo ra giữ giống hệt 3 kênh trong texture nền. Để tạo được hiệu ứng, kênh alpha là điểm cần lưu ý.

Theo phương pháp hiện thực trong bài viết đã nhân giá trị của 2 kênh alpha trong texture nền và texture mặt nạ để tính toán giá trị kênh alpha trong màu cuối cùng.

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

bool MaskedSprite::init(const char* textureName, const char* maskedTextureName)
{
	if(!CCSprite::initWithFile(textureName))
	{
		return false;
	}

	_maskedTexture = CCTextureCache::sharedTextureCache()->addImage(
                                                        maskedTextureName);
	_maskedTexture->retain();

	CCGLProgram* pProgram = new CCGLProgram();
	pProgram->initWithVertexShaderFilename("MaskedSprite.vert",
                                                           "MaskedSprite.frag");

	setShaderProgram(pProgram);

	pProgram->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
	pProgram->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);
	pProgram->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);

	pProgram->link();
	pProgram->updateUniforms();

	_locTexture = glGetUniformLocation(pProgram->getProgram(), "u_texture");
	_locMask = glGetUniformLocation(pProgram->getProgram(), "u_mask");

	pProgram->release();

	return true;
}

Ở phần khởi tạo này khởi tạo shader đã viết ở trên cho component và lấy giá trị 2 vị trí của 2 texture uniform (texture nền và texture làm mặt nạ). 2 vị trí này sẽ sử dụng cho bước render.

Đây là phần render trong component:

void MaskedSprite::draw()
{
	CC_NODE_DRAW_SETUP();

	ccGLBlendFunc( m_sBlendFunc.src, m_sBlendFunc.dst );

	glActiveTexture(GL_TEXTURE0);
	glBindTexture( GL_TEXTURE_2D,  m_pobTexture->getName() );
	glUniform1i(_locTexture, 0);

	glActiveTexture(GL_TEXTURE1);
	glBindTexture( GL_TEXTURE_2D,  _maskedTexture->getName() );
	glUniform1i(_locMask, 1);


	ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );

	long offset = (long)&m_sQuad;

#define kQuadSize sizeof(m_sQuad.bl)

	// vertex
	int diff = offsetof( ccV3F_C4B_T2F, vertices);
	glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE,
                                    kQuadSize, (void*) (offset + diff));

	// texCoods
	diff = offsetof( ccV3F_C4B_T2F, texCoords);
	glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE,
                                    kQuadSize, (void*)(offset + diff));

	// color
	diff = offsetof( ccV3F_C4B_T2F, colors);
	glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE,
                                    kQuadSize, (void*)(offset + diff));

	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	CHECK_GL_ERROR_DEBUG();
}
  • Dòng 3: thiết đặt program cho trạng thái hiện tại là program đã được khởi tạo ở trên.
  • Dòng 7-13: bind 2 texture nền và texture mặt nạ.
  • Dòng 16: enable các thuộc tính được dùng của các đỉnh (trong trường hợp này là thuộc tính vị trí, màu sắc, toạ độ của texture).
  • Dòng 18-35: vẽ component.

Code

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

Cách dùng Component

CCSize winSize = CCDirector::sharedDirector()->getWinSize();

MaskedSprite* pSprite = MaskedSprite::create("HelloWorld.png",
                                                "Mask.png");
pSprite->setPosition(ccp(winSize.width/2.0f, winSize.height/2.0f));
this->addChild(pSprite);
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024