Пример #1
0
/*!****************************************************************************
 @Function		RenderScene
 @Return		bool		true if no error occured
 @Description	Main rendering loop function of the program. The shell will
				call this function every frame.
				eglSwapBuffers() will be performed by PVRShell automatically.
				PVRShell will also manage important OS events.
				Will also manage relevent OS events. The user has access to
				these events through an abstraction layer provided by PVRShell.
******************************************************************************/
bool OGLES2StencilBuffer::RenderScene()
{
	m_fAngle += 0.005f;

	// Clear the color, depth and stencil buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	// Use shader program
	glUseProgram(m_ShaderProgram.uiId);

	/*
		Set up the transformation matrices for our two shapes (the cylinder and the sphere)
	*/
	PVRTMat4 mSphere, mCylinder;
	PVRTMat4 mTrans, mRotZ, mRotX, mScale;

	mScale = PVRTMat4::Scale((float)PVRShellGet(prefHeight)/(float)PVRShellGet(prefWidth), 1.0f, 1.0f);
	mRotZ  = PVRTMat4::RotationX(m_fAngle);

	mSphere = mRotZ * mScale;

	mTrans = PVRTMat4::Translation(-0.4f, -0.5f, 0.0f);
	mRotZ  = PVRTMat4::RotationZ(m_fAngle);
	mRotX  = PVRTMat4::RotationX(m_fAngle);

	mCylinder = mScale * mRotX * mRotZ * mTrans;

	// Bind texture and set transform
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, m_uiStoneTex);
	glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, mSphere.ptr());

	// Set culling to cull the back faces
	glCullFace(GL_BACK);

	/*
		Draw the sphere

		This sphere is textured with the stone texture and will be visible outside the stencil volume as
		we are drawing a second sphere with a green tiles texture everywhere within the stencil
		geometry.

		Also this sphere is used to set the depth values in the Z-Buffer.
	*/
	m_Sphere.DrawMesh(0);


	/*
		Enable the stencil test, disable color and depth writes
	*/
	glEnable(GL_STENCIL_TEST);
	glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
	glDepthMask(GL_FALSE);

	/*
		What we are going to do is draw a volume (a cylinder) so that all front faces that are in front of
		the already rendered geometry (the sphere) increase the stencil value by one, while all back faces
		that are in front of the rendered geometry decrease the stencil value. This way only surfaces that
		intersect the stencil volume will get a stencil value != 0.

		Since OpenGL ES 2.0 offers two-sided stencil, we can do this in a single pass.
	*/
	// Disable culling as we want to use the back and front faces of the geometry.
	glDisable(GL_CULL_FACE);

	/*
		glStencilFunc tells OGLES2 the type of per-pixel test that we want to do. In the case below GL_ALWAYS says we
		want the test to always pass. The third value is the mask value which is ANDed with the second value
		(the reference value) to create the value that is put in the stencil buffer.

		Alternative values for the first value are

		GL_NEVER which causes the test to never pass.
		GL_LESS	    Passes if (	ref & mask ) < ( stencil & mask ).
		GL_LEQUAL	Passes if (	ref & mask ) < ( stencil & mask ).
		GL_GREATER	Passes if (	ref & mask ) > ( stencil & mask ).
		GL_GEQUAL	Passes if (	ref & mask ) > ( stencil & mask ).
		GL_EQUAL	Passes if (	ref & mask ) = ( stencil & mask ).
		GL_NOTEQUAL	Passes if (	ref & mask ) / ( stencil & mask ).

		A call to glStencilFunc is the same as calling glStencilFuncSeparate with GL_FRONT_AND_BACK
	*/
	glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

	/*
		glStencilOp has 3 parameters. The first parameter specifies the action to take if the
		stencil test fails. The second specifies the stencil action to take if the stencil test passes
		but the depth test fails. The third one specifies the stencil action when the stencil test and
		the depth test pass, or when the stencil test passes and their is no depth testing done.

		These three parameters can be set to one of the following

		GL_KEEP Keeps the current value.
		GL_ZERO Sets the stencil buffer value to zero.
		GL_REPLACE Sets the stencil buffer value to ref, as specified by glStencilFunc.
		GL_INCR Increments the current stencil buffer value. Clamps to the maximum representable unsigned value.
		GL_DECR Decrements the current stencil buffer value. Clamps to zero.
		GL_INCR_WRAP Increments the current stencil buffer value and wraps round to zero if the value is above the maximum.
		GL_DECR_WRAP Decrements the current stencil buffer value and wraps it round to the maximum if it goes below zero.
		GL_INVERT	Bitwise inverts the current stencil buffer value.

		We're going to do our stencil operations in one pass so we need to specify an operation for each face type
		using the glStencilOpSeparate function which takes an extra variable. This variable will define
		which face type we'll work on and it can be set to

		GL_FRONT
		GL_BACK
		GL_FRONT_AND_BACK

		In our case we are going to use GL_INCR_WRAP for the front faces and GL_DECR_WRAP for the back faces.
		As the geometry will be processed in the order it is submitted we can't guarantee that we'll do all
		the INCR operations first therefore we are using INCR_WRAP and DECR_WRAP so the values don't get clamped
		at the minimum and maximum possible values.
	*/
	glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
	glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);

	glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, mCylinder.ptr());

	m_Cylinder.DrawMesh(0);


	/*
		Enable drawing to the colour buffer again as what we draw now we want to be visible.
		Switch back to back face culling and enable the depth test again.
	*/
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glDepthMask(GL_TRUE);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	/*
		Set the stencil test to draw only pixels that are inside the stencil volume
	*/
	glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	glBindTexture(GL_TEXTURE_2D, m_uiTileTex);
	glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, mSphere.ptr());

	m_Sphere.DrawMesh(0);

	/* Disable the stencil test as it is no longer needed.*/
	glDisable(GL_STENCIL_TEST);

	/*
		Draw the cylinder with alpha blending, back faces first then front faces
	*/
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBindTexture(GL_TEXTURE_2D, m_uiCylinderTex);
	glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, mCylinder.ptr());

	// Draw back faces of the cylinder
	glCullFace(GL_FRONT);
	m_Cylinder.DrawMesh(0);

	// Draw the front faces
	glCullFace(GL_BACK);
	m_Cylinder.DrawMesh(0);

	// Disable blending as it is no longer required
	glDisable(GL_BLEND);

	// Displays the demo name using the tools. For a detailed explanation, see the training course IntroducingPVRTools
	m_Print3D.DisplayDefaultTitle("Stencil Buffer", "", ePVRTPrint3DLogoIMG);
	m_Print3D.Flush();

	return true;
}