用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

猜你喜欢

转载自blog.csdn.net/weixin_45776473/article/details/126237732
今日推荐