游戏模型模糊高亮边缘效果实现shader

游戏中因为有时怪物太多,常常需要对攻击的怪物高亮,对于游戏体验都有很大的提升,也增加了玄幻色彩,如下所示:


网上也有不少实现这种效果的例子,《introduce  to DirectX3D game programming》 书中介绍有法线放大的方法,这种方法对于规则模型,比如茶壶(teapot)是有很好的效果,见博客中所示, 但是游戏中也有不少NPC 是穿有衣服,衣袂飘飘, 此时这种片状的模型在边缘是没有法线的,所以往往无法描边,最后的效果必然是不完整的。


鉴于此,重新思考法线放大方法。因为法线放大最终的效果就是为了实现把单色渲染的模型背景图沿周围扩大,我们可以采用别的方法达到同样的效果。对图像熟悉的应该会想到膨胀(inflation)。所以下面所讲的模糊高亮效果就是通过下面几个步骤实现:

1. 单色渲染模型到一张RTT_1

2.  设置currentTarget 为RTT_2,  getTargetTexture(RTT_1),   进行膨胀

3. 设置currentTarget 为RTT_1,  getTargetTexture(RTT_2),   进行高斯模糊

3. 最后一步就是把处理后的纹理贴到游戏中

下面具体讲解下shader 书写:

1. 单色渲染pass, 在顶点pass 中,主要是做坐标转换

// rimback pass

struct Rimback_VS_OUTPUT
{
	float4 pos : POSITION0;
	float2 uv  : TEXCOORD0;
};

Rimback_VS_OUTPUT Rimback_vs(VertexInput input)
{
	// localspace转换到worldspace,并且蒙皮
	WorldVertex wv = VertexInputToWorld(input);
	Rimback_VS_OUTPUT output;

	float3 pos = wv.worldPos.xyz;

	output.pos = mul(float4(pos, 1), vp_mat);
	output.uv = input.uv0;
	return output;
}

float4 Rimback_ps(float2 uv : TEXCOORD0): color0   
{
	float4 color = tex2D(modelTex, uv);
	clip(color.a-0.5);  
	color.xyz= edgeColor.xyz;
	//clip(color.a);  
	return float4(color.rgb,1);
}

其中 VertexInputToWorld(VertexInput input) 函数实现描边随人物移动而移动,相当于给描边加上骨骼,函数中主要也是实现input 和 骨骼矩阵 之间的运算

2. 膨胀,关于膨胀原理,就是通过周围搜索,增加像素,填补空白。本例子中,是通过求取周围像素的alpha 值,因为步骤1得到单色渲染纹理的alpha为1,周围取到的alpha值是0,这样通过计算,得到边缘部分的alpha 在0~1 之间。通过阈值可以控制膨胀效果。

float4 pengzhang_ps(float2 uv : TEXCOORD0) : color0
{
	const float2 flation[25] =
	{
		-2.70,-2.70,
		-2.70,-1.70,
		-2.70, 0.70,
		-2.70, 1.70,
		-2.70, 2.70,
		-1.70,-2.70,
		-1.70,-1.70,
		-1.70, 0.70,
		-1.70, 1.70,
		-1.70, 2.70,
		0.70,-2.70,
		0.70,-1.70,
		0.70, 0.70,
		0.70, 1.70,
		0.70, 2.70,
		1.70,-2.70,
		1.70,-1.70,
		1.70, 0.70,
		1.70, 1.70,
		1.70, 2.70,
		2.70,-2.70,
		2.70,-1.70,
		2.70, 0.70,
		2.70, 1.70,
		2.70, 2.90,
	};
	float4 outColor = tex2D(blurTex, uv);

		for(int i = 0; i< 25; i++)
		{
			float4 color = tex2D(blurTex, uv + flation[i].xy * viewport_inv_size.xy);
				// add color to flation
				if(color.a >= 0.98)
					outColor = color;
		}

		return outColor;
}

3. 高斯膨胀,原理比较简单,可以自己网上查找资料

float4 edegblur_ps(float2 uv : TEXCOORD0) : color0
{
    // 0.7 is for pixel bias
	const float4 samples[9] = {
		-3.70, -3.70, 0, 1.0/16.0,
		-3.70,  3.70, 0, 1.0/16.0,
		3.70, -3.70, 0, 1.0/16.0,
		3.70,  3.70, 0, 1.0/16.0,
		-3.70,  0.70, 0, 2.0/16.0,
		3.70,  0.70, 0, 2.0/16.0,   
		0.70, -3.70, 0, 2.0/16.0,
		0.70,  3.70, 0, 2.0/16.0,
		0.70,  0.70, 0, 4.0/16.0
	};

	float4 col = float4(0, 0, 0, 0);
		//Sample and output the averaged colors
		for(int i=0;i<9;i++)
			col += samples[i].w * tex2D(blurTex, uv + samples[i].xy * viewport_inv_size.xy);
	return float4(edgeColor.r,edgeColor.g,edgeColor.b,col.a);
}

4. 贴图,

struct SceneBlend_VS_OUTPUT
{
	float4 pos	: POSITION0;
	float2 uv	: TEXCOORD0;
	float4 uvProj : TEXCOORD1;
};

VS_OUTPUT  scene_blend__vs(VS_INPUT vert)
{
	VS_OUTPUT vsout;

	vsout.pos = float4(vert.pos,1);
	vsout.uv  = vert.uv;

	vsout.pos.z = 1.0f;

	return vsout;
}


float4 scene_blend_ps(VS_OUTPUT input):color0
{
	float alpha = tex2D(blurTex,input.uv).a;
	clip(1.0 - (alpha + 0.2f));// 此处是实现显示轮廓边缘,中间部分alpha>0.8 的不显示
	return tex2D(blurTex, input.uv);
}


最后,关于遮挡描边的效果,在步骤1,即单色渲染的时候,只需要打开深度检测,这样渲染的纹理,仅仅是模型可见部分,而不可见部分就没有渲染进去,当然后面的膨胀,模糊都没有它们的事情了。最后的效果图,如下所示:



关于pass最后的效率情况,如果bur效果不明显,可以通过重复blur。如果pass影响游戏帧数,耗内存太多,可以考虑对blur 进行横向以及纵向分开pass。

以上思路仅供参考,希望对游戏中开发有所启示。


参考信息:

http://www.gamedev.net/topic/655543-outline-glow-effect/

http://wiki.unity3d.com/index.php/Silhouette-Outlined_Diffuse

https://en.wikipedia.org/wiki/Gaussian_blur

https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
实现模型边缘高亮可以使用描边Shader,而实现穿透别的模型可以使用深度测试。下面是一个简单的实现步骤: 1. 创建一个新的 Shader,添加描边效果。这里使用 Toon/Lit Outline Shader 作为基础。 2. 在 Shader 中添加深度测试,这样就可以让该模型在渲染时穿透其他模型。添加方法如下: ``` Tags {"Queue"="Transparent+1"} ZWrite Off Blend SrcAlpha OneMinusSrcAlpha ``` 这里的 Tags 指定了渲染队列,这样才能让该模型在其他模型之后渲染。ZWrite Off 指定了关闭深度写入,这样就可以让该模型在渲染时穿透其他模型。Blend 指定了混合模式,这里使用了半透明混合。 3. 在 Unity 中将需要高亮模型Shader 替换为上一步创建的 Shader。 4. 在代码中使用深度测试来检测是否需要高亮边缘。可以在 Update 函数中使用 Raycast 进行检测,如果射线碰撞到了该模型,则开启描边效果。 ``` RaycastHit hit; if (Physics.Raycast(transform.position, transform.forward, out hit)) { if (hit.collider.gameObject == gameObject) { // 开启描边效果 outline.enabled = true; } else { // 关闭描边效果 outline.enabled = false; } } ``` 在上面的代码中,我们使用了 Physics.Raycast 来发射一条射线,检测是否碰撞到了该模型。如果碰撞到了,则开启描边效果。否则,关闭描边效果。 这样就可以实现模型边缘高亮并且可以穿透别的模型效果了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不负初心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值