Unity3D 屏幕空间雪场景Shader渲染

原创 2017年07月14日 22:38:38

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

游戏中会出现各种各样的场景,比如雪场景,草地场景,城市场景等,这些场景通常的做法是通过美术利用Max工具建模实现的,在这里我们可以使用Shader去渲染,这样可以减少美术的工作量并且能优化效率。先给读者展示如下所示:

3D model house village with trees in the background in Unity

正常的场景效果,下面再给你看一副利用Shader的雪场景效果:


这两幅场景是相同的,唯一的区别是第二幅采用了雪效果场景,其他的纹理没有做任何改变,第二幅使用的就是屏幕空间的场景渲染,效果非常不错,下面给读者介绍一下它的实现原理:

理论很简单假设一旦渲染像素的正常面朝上(地面,屋顶等),则应该绘制雪。如果像素的正常面向任何其他方向,那么在雪纹理和原始纹理之间也应该有一个平缓的过渡 。

  在实现该Shader之前需要做一些设置,首先将Rendering Path设置为Deferred (延迟渲染),如果将其设置成forward Rendering(前向渲染)在使用Shader时会出现问题。

  在代码中将Camera.depthTextureMode设置成DepthNormals是为了将允许我们读取屏幕深度和法线。实现第二幅图效果只需要一个脚本和一个Shader文件就可以。下面实现屏幕渲染的脚本,代码如下所示:

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class ScreenSpaceSnow : MonoBehaviour
{

	public Texture2D SnowTexture;

	public Color SnowColor = Color.white;

	public float SnowTextureScale = 0.1f;

	[Range(0, 1)]
	public float BottomThreshold = 0f;
	[Range(0, 1)]
	public float TopThreshold = 1f;

	private Material _material;

	void OnEnable()
	{
		// dynamically create a material that will use our shader
		_material = new Material(Shader.Find("TKoU/ScreenSpaceSnow"));

		// tell the camera to render depth and normals
		GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;
	}

	void OnRenderImage(RenderTexture src, RenderTexture dest) 
	{
		// set shader properties
		_material.SetMatrix("_CamToWorld", GetComponent<Camera>().cameraToWorldMatrix);
		_material.SetColor("_SnowColor", SnowColor);
		_material.SetFloat("_BottomThreshold", BottomThreshold);
		_material.SetFloat("_TopThreshold", TopThreshold);
		_material.SetTexture("_SnowTex", SnowTexture);
		_material.SetFloat("_SnowTexScale", SnowTextureScale);

		// execute the shader on input texture (src) and write to output (dest)
		Graphics.Blit(src, dest, _material);
	}
}
接下来实现Shader代码编写,我们需要把建筑物表面向上的铺上雪,所有法线朝上的表面都将覆盖雪。相机已经设置了生成深度法线贴图,所以现在直接获取即可。代码如下:

sampler2D _CameraDepthNormalsTexture;
查看Unity官方文档可以了解该命名的意义:
深度贴图可以作为一个着色器的全局着色器属性进行采样。通过声明名为_CameraDepthTexture的采样器,就能够采样相机的主深度纹理。
_CameraDepthTexture总是引用相机的主深度贴图。
获取法线的代码函数:
half4 frag (v2f i) : SV_Target
{
    half3 normal;
    float depth;
 
    DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), depth, normal);
    normal = mul( (float3x3)_CamToWorld, normal);
 
    return half4(normal, 1);
}
Unity文档解释深度和法线的数据都打包为16位。这里需要像代码那样调用DecodeDepthNormal方法进行解包。

这个方法检索的是相机空间的法线。也就是说,如果旋转屏幕相机,那么法线朝向也会改变。脚本中将法线乘以_CamToWorld 矩阵就是为了避免这种情况。它会将法线从相机空间转换为世界空间,这样就不再依赖于相机的透视。

为了让着色器正确编译就必须返回一些东西,所以上面的代码设置了返回语句。这样也便于预览结果以确认计算是否正确。

暂时渲染为RGB图像。在Unity中,Y轴是默认向上的。图中绿色部分表示Y坐标轴的值,现在将其转换为雪量的因子。

half4 frag (v2f i) : SV_Target
{
    half3 normal;
    float depth;
 
    DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), depth, normal);
    normal = mul( (float3x3)_CamToWorld, normal);
 
    half snowAmount = normal.g;
    half scale = (_BottomThreshold + 1 - _TopThreshold) / 1 + 1;
    snowAmount = saturate( (snowAmount - _BottomThreshold) * scale);
 
    return half4(snowAmount, snowAmount, snowAmount, 1);
}
这里会用到绿色分量。接下来配置积雪覆盖区域顶部和底部的阀值,以便于调整场景的积雪量。

接下来介绍纹理,如果没有纹理,雪看起来会不真实。最难的部分就是将2D纹理(屏幕空间)应用到3D物体上。一种方法是获取像素的世界坐标,然后将世界坐标的X和Z值作为纹理坐标。

half4 frag (v2f i) : SV_Target
{
    half3 normal;
    float depth;
 
    DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), depth, normal);
    normal = mul( (float3x3)_CamToWorld, normal);
 
    // find out snow amount
    half snowAmount = normal.g;
    half scale = (_BottomThreshold + 1 - _TopThreshold) / 1 + 1;
    snowAmount = saturate( (snowAmount - _BottomThreshold) * scale);
 
    // find out snow color
    float2 p11_22 = float2(unity_CameraProjection._11, unity_CameraProjection._22);
    float3 vpos = float3( (i.uv * 2 - 1) / p11_22, -1) * depth;
    float4 wpos = mul(_CamToWorld, float4(vpos, 1));
    wpos += float4(_WorldSpaceCameraPos, 0) / _ProjectionParams.z;
 
    half3 snowColor = tex2D(_SnowTex, wpos.xz * _SnowTexScale * _ProjectionParams.z) * _SnowColor;
 
    return half4(snowColor, 1);
}
这里涉及到一些数学知识,您只需知道vpos是视口坐标,wpos是由视口坐标与_CamToWorld矩阵相乘而得到的世界坐标,并且它通过除以远平面的位置(_ProjectionParams.z)来转换为有效的世界坐标。最后使用XZ坐标乘以可配置参数_SnowTexScale和远平面,来计算雪的颜色并获取适当的值。

下面将积雪与场景进行合并:

half4 frag (v2f i) : SV_Target
{
    half3 normal;
    float depth;
 
    DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), depth, normal);
    normal = mul( (float3x3)_CamToWorld, normal);
 
    // find out snow amount
    half snowAmount = normal.g;
    half scale = (_BottomThreshold + 1 - _TopThreshold) / 1 + 1;
    snowAmount = saturate( (snowAmount - _BottomThreshold) * scale);
 
    // find out snow color
    float2 p11_22 = float2(unity_CameraProjection._11, unity_CameraProjection._22);
    float3 vpos = float3( (i.uv * 2 - 1) / p11_22, -1) * depth;
    float4 wpos = mul(_CamToWorld, float4(vpos, 1));
    wpos += float4(_WorldSpaceCameraPos, 0) / _ProjectionParams.z;
 
    wpos *= _SnowTexScale * _ProjectionParams.z;
    half3 snowColor = tex2D(_SnowTex, wpos.xz) * _SnowColor;
 
    // get color and lerp to snow texture
    half4 col = tex2D(_MainTex, i.uv);
    return lerp(col, half4 (snowColor,1.0f), snowAmount);
}
上述代码获取原始颜色,并使用snowAmount进行插值渐变为snowColor 。


最后一步:将_TopThreshold设为0.6:


全屏效果见下图:




完整的代码下载地址:链接:http://pan.baidu.com/s/1qYTs0cG 密码:uw1k





版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

Unity3D 海水多线程渲染算法实现

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等...

Unity3D教你制作Bezier和Spine曲线编辑器三

继续接着介绍曲线编辑器的制作,上篇博客介绍了关于Bezier曲线的制作,接下来给读者介绍Spine B样条曲线之作。 如果要创建复杂的曲线,我们需要连接多个曲线,这样的构造称为样条。让我们通过复制Be...

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

网路游戏之物理模拟

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等...
  • jxw167
  • jxw167
  • 2017-07-24 11:41
  • 1356

Unity3D快速实现UI架构设计二

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》...

Unity3D引擎之Shader Forge应用

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》...
  • jxw167
  • jxw167
  • 2017-04-04 17:52
  • 2382

Unity3D之高级渲染-Shader Forge增强版

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》...
  • jxw167
  • jxw167
  • 2017-04-05 19:25
  • 2176

Unity3D Shader之路 写Shader前必须要知道的事情3 ShaderForge的简单使用

版本:unity 5.4.1  语言:Unity Shader Shader Forge版本:1.32   总起: 在具体介绍Shader之前准备再写一篇有关于ShaderForge的,虽然我们可能使...

网络游戏之快照插值物理模拟

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)