[DirectX]Programming.Role.Playing.Games:02_04_AlphaTest

    AlphaTest,在Programming.Role.Playing.Games.with.DirectX书上讲的是一张带孔的图片,书上只给了个实现代码,而且用的是ColorKey这玩意,没有详细的介绍,源码也没有,图片也没有,难道需要我手绘,那就尴尬了,还好想起我之前学《UnitytShader入门精要》中有AlphaTest章节,刚好把那张贴图拿过用。还是老规则,左边的是用固定管线实现的,右边是用Shader实现的。右边的变化还是比较明显的。

    因为书上没详细代码,我只好以自己的方式来实现固定管线的代码,是在之前的Draw3D代码上进行修改的,用Cube来做效果会好点。跟之前的AlphaBlend一样,开启和关闭AlphaTest很简单,SetRenderState(D3DRS_ALPHATESTENABLE,bIsEnable),bIsEnable是bool值,是否开启。

    来看下渲染的代码:

    固定管线:

void ModelClass::Render(IDirect3DDevice9* device, float time, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, float alphaTestValue)
{
	D3DXMATRIX xRotationMatrix, yRotationMatrix;
	D3DXMATRIX translationMatrix;

	// Rotation
	::D3DXMatrixRotationX(&xRotationMatrix, D3DX_PI * 0.25f);
	::D3DXMatrixRotationY(&yRotationMatrix, -time);

	// Translate
	::D3DXMatrixTranslation(&translationMatrix, -2.0f, 0.0f, 0.0f);

	::D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &xRotationMatrix);
	::D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &yRotationMatrix);
	::D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &translationMatrix);

	device->SetTransform(D3DTS_WORLD, &worldMatrix);
	device->SetTransform(D3DTS_VIEW, &viewMatrix);
	device->SetTransform(D3DTS_PROJECTION, &projectionMatrix);

	device->SetRenderState(D3DRS_LIGHTING, false);

	device->SetTexture(0, m_texture->GetTexture());

	device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
	device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
	device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

	// Disable cull the back
	device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	// Enable the alpha test
	device->SetRenderState(D3DRS_ALPHATESTENABLE, true);
	device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
	device->SetRenderState(D3DRS_ALPHAREF, (DWORD)(alphaTestValue * 255));

	device->SetStreamSource(0, m_vertexBuffer, 0, sizeof(VertexType));
	device->SetFVF(VERTEX_FVF);

	device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, m_vertexCount / 3);

	device->SetTexture(0, nullptr);

	// Don't forget to disable the alpha test
	device->SetRenderState(D3DRS_ALPHATESTENABLE, false);
}

    代码很简单,在之前Draw3D上加了几句AlphaTest代码而已,只是的注意下SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)这句,这句代码的功能是关闭剔除背面的功能,意思是就是需要绘制背面,如果不写这句的话,dx是默认剔除背面的,你就会看不到背面,得不到上面的效果,有兴趣的可以试一下。

    还是跟之前一样,shader代码之后再来解释下SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL)SetRenderState(D3DRS_ALPHAREF, (DWORD)(alphaTestValue * 255))函数。

    Shader渲染:

void ShaderModelClass::Render(IDirect3DDevice9* device, float time, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, float alphaTestValue)
{
	D3DXMATRIX xRotationMatrix, yRotationMatrix;
	D3DXMATRIX translationMatrix;
	bool result;
	UINT passMaxNum;

	// Rotation
	::D3DXMatrixRotationX(&xRotationMatrix, D3DX_PI * 0.25f);
	::D3DXMatrixRotationY(&yRotationMatrix, time);

	// Translate
	::D3DXMatrixTranslation(&translationMatrix, 2.0f, 0.0f, 0.0f);

	::D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &xRotationMatrix);
	::D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &yRotationMatrix);
	::D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &translationMatrix);

	device->SetStreamSource(0, m_vertexBuffer, 0, sizeof(VertexType));
	device->SetFVF(VERTEX_FVF);

	result = m_colorShader->Render(device, m_texture->GetTexture(), time, worldMatrix, viewMatrix, projectionMatrix, alphaTestValue);
	if (!result)
		return;

	m_colorShader->GetEffect()->Begin(&passMaxNum, 0);
	for (UINT pass = 0; pass < passMaxNum; ++pass)
	{
		m_colorShader->GetEffect()->BeginPass(pass);
		device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, m_vertexCount / 3);
		m_colorShader->GetEffect()->EndPass();
	}
	m_colorShader->GetEffect()->End();
}

    跟之前Draw3D的代码几乎没什么变化,只是向effect中传了AlphaTestValue值,这个值在GraphicsClass中定义的,后面会说到的。

#pragma once

#include "D3DClass.h"
#include "ModelClass.h"
#include "ShaderModelClass.h"

const bool FULL_SCREEN = false;
const UINT DEFAULT_SCREEN_WIDTH = 1280;
const UINT DEFAULT_SCREEN_HEIGHT = 720;
const float AlphaTestValue = 0.6f;

class GraphicsClass
{
public:
	GraphicsClass();
	GraphicsClass(const GraphicsClass& other);
	~GraphicsClass();

	bool Initialize(int screenWidth, int screenHeight, HWND hwnd, bool fullScreen);
	void Shutdown();
	bool Frame();

private:
	D3DClass* m_direct3D;
	ModelClass* m_model;
	ShaderModelClass* m_shaderModel;
};

    Shader代码:

texture modelTexture;
float time;
float4x4 worldMatrix;
float4x4 viewMatrix;
float4x4 projectionMatrix;
float alphaTestValue;

sampler ModelTextureSampler = sampler_state
{
	Texture = <modelTexture>;
	MipFilter = POINT;
	MinFilter = POINT;
	MagFilter = POINT;
	//MipFilter = LINEAR;
	//MinFilter = LINEAR;
	//MagFilter = LINEAR;
	//MipFilter = ANISOTROPIC;
	//MinFilter = ANISOTROPIC;
	//MagFilter = ANISOTROPIC;
};

struct VertexInputType
{
	float4 vertex : POSITION;
	float2 texcoord : TEXCOORD0;
};

struct PixelInputType
{
	float4 pos : POSITION;
	float2 texcoord : TEXCOORD0;
};

PixelInputType ColorVertexShader(VertexInputType input)
{
	PixelInputType output;

	float4x4 worldViewMaxtrix = mul(worldMatrix, viewMatrix);
	float4x4 worldViewProjectionMatrix = mul(worldViewMaxtrix, projectionMatrix);
	output.pos = mul(input.vertex, worldViewProjectionMatrix);
	output.texcoord = input.texcoord;

	return output;
}

float4 ColorPixelShader(PixelInputType input) : COLOR
{
	float param = max(0.5, cos(time));
	float4 color = tex2D(ModelTextureSampler, input.texcoord) * param;
	if (color.a < 0.5)
		discard;

	return color;
}

technique ColorTechnique
{
	pass pass0
	{
		CullMode = None;
		VertexShader = compile vs_2_0 ColorVertexShader();
		PixelShader = compile ps_2_0 ColorPixelShader();
	}
}

    Shader代码与之前Draw3D的几乎一样,只是在像素着色器(PixelShader)和Pass中有稍微的区别。param是一个参数,用来改变当前的alpha值。重点来了,之前SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL)SetRenderState(D3DRS_ALPHAREF, (DWORD)(alphaTestValue * 255))函数,在shader中就可以用标红的代码来解释,如果不满足条件就抛弃,意味着不渲染。我这句代码的意思就是,取当前像素的alpha之和0.5来进行比较,如果满足条件就进行渲染,不满足就抛弃,不渲染。

    D3DCMP_GREATEREQUAL的意思就是大于等于,(DWORD)(alphaTestValue * 255)就是一个参考值,所以SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL)SetRenderState(D3DRS_ALPHAREF, (DWORD)(alphaTestValue * 255))函数的意思就是,当前像素的alpha值大于等于(DWORD)(alphaTestValue * 255))的时候才进行渲染,跟shader代码是不是很像啊。D3DRS_ALPHAFUNC可以理解为一个条件,比如等于,小于啊什么的,D3DRS_ALPHAREF则是参数设置,连起来就是小于什么参数或者等于什么参数才进行渲染。

    为什么shader中我写死了0.5这个值,不用代码传过来的alphaTestValue这个值的原因是在测试的时候,发现这个效果很炫,用这个alphaTestValue值效果不是很好,就没有用,如果有兴趣的话,可以将0.5改成alphaTestValue值,在随意调整alphaTestValue这个值,测试下效果。

源码下载:下载地址

猜你喜欢

转载自blog.csdn.net/zp288105109a/article/details/80869358