用Unity实现displacement

用Unity实现displacement

除了使用normal mapping和parallax mapping以外,我们可以利用tessellation,首先为物体表面生成若干新的顶点,然后对顶点的信息进行调整,来真正地增加物体表面细节的丰富程度。为此,需要对vertex shader修改,在一开始就调整传入的顶点信息:

		float displacement = tex2Dlod(_DisplacementMap, float4(i.uv.xy, 0, 0)).g;
		displacement = (displacement - 0.5) * _DisplacementStrength;
		v.normal = normalize(v.normal);
		v.vertex.xyz += v.normal * displacement;

在这里插入图片描述

可以看到效果还不错。但是如果要加上阴影,就会出现如下的情况:

在这里插入图片描述

在这里插入图片描述

第一张图使用的是forward rendering path,而第二张图使用的是deferred rendering path。如果选择前向渲染路径,则物体接收和投射的阴影都是变换顶点之前的;如果选择延迟渲染路径,物体接收阴影的效果是正确的,但投射还是变换顶点之前的。投射阴影有问题的原因不难猜到,shadow caster pass也需要实现displacement。不过,接收阴影,为什么会出现前向渲染错误而延迟渲染正确的情况呢?回忆一下,所谓接收阴影,就是要从shadow map中取出深度信息进行深度比较,那问题应该就出在这里。对于前向渲染,会调用shadow caster pass来渲染depth buffer;而延迟渲染,会调用deferred pass来渲染GBuffer。

在这里插入图片描述

在这里插入图片描述

补上shadow caster pass的displacement之后,效果如下:

在这里插入图片描述

在tessellation的过程中,如果生成的顶点压根不在摄像机的可见范围内,那后续的处理过程都是极大的浪费。我们可以计算生成顶点到摄像机六个剪裁面的距离,来判断该点是否在摄像机的视锥体内,如果不是,就可以跳过tessellation步骤:

TessellationFactors MyPatchConstantFunction (
	InputPatch<TessellationControlPoint, 3> patch
) {
	float3 p0 = mul(unity_ObjectToWorld, patch[0].vertex).xyz;
	float3 p1 = mul(unity_ObjectToWorld, patch[1].vertex).xyz;
	float3 p2 = mul(unity_ObjectToWorld, patch[2].vertex).xyz;
	TessellationFactors f;

	float bias = -0.5 * _DisplacementStrength;

	if (TriangleIsCulled(p0, p1, p2, bias)) {
		f.edge[0] = f.edge[1] = f.edge[2] = f.inside = 0;
	}
	else {
	    ...
	}
	return f;
}

我们选择在tessellation的patch const function中做这件事。只要TessellationFactors的各分量都为0,tessellation就不会在这个三角形上执行了。这里我们还加入了一个bias的机制,防止经过displacement的顶点,因为偏移到了不可见的范围,被裁剪掉了。避免表现上看上去会比较奇怪。

最后来看一下TriangleIsCulled这个函数的实现:

bool TriangleIsBelowClipPlane (
	float3 p0, float3 p1, float3 p2, int planeIndex, float bias
) {
	float4 plane = unity_CameraWorldClipPlanes[planeIndex];
	return
		dot(float4(p0, 1), plane) < bias &&
		dot(float4(p1, 1), plane) < bias &&
		dot(float4(p2, 1), plane) < bias;
}

bool TriangleIsCulled (float3 p0, float3 p1, float3 p2, float bias) {
	return
		TriangleIsBelowClipPlane(p0, p1, p2, 0, bias) ||
		TriangleIsBelowClipPlane(p0, p1, p2, 1, bias) ||
		TriangleIsBelowClipPlane(p0, p1, p2, 2, bias) ||
		TriangleIsBelowClipPlane(p0, p1, p2, 3, bias);
}

这里,只有三角形的3个点全部在某个剪裁面之外,才会被认为需要裁剪掉。

如果你觉得我的文章有帮助,欢迎关注我的微信公众号:我是真的想做游戏啊

Reference

[1] Surface Displacement

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值