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

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

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

正常的场景效果,下面再给你看一副利用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 屏幕空间雪场景Shader渲染的更多相关文章

  1. Unity3d 屏幕空间人体皮肤知觉渲染&次表面散射Screen-Space Perceptual Rendering & Subsurface Scattering of Human Skin

    之前的人皮渲染相关 前篇1:unity3d Human skin real time rendering 真实模拟人皮实时渲染 前篇2:unity3d Human skin real time ren ...

  2. Unity3d 着色器语法(Shader)

    Shader "name" { [Properties] Subshaders [Fallback] } 定义了一个着色器.着色器拥有一个 Properties 的列表.着色器包含 ...

  3. Three.js粒子特效,shader渲染初探(一篇非常详细的介绍)

    Three.js粒子特效,shader渲染初探 转载来源:https://juejin.im/post/5b0ace63f265da0db479270a 这大概是个序 关于Three.js,网上有不多 ...

  4. 在Unity中实现屏幕空间阴影(1)

    接着上篇文章,我们实现了SSR效果. 其中的在屏幕空间进行光线追踪的方法是通用的.借此我们再实现一种屏幕空间的效果,即屏幕空间阴影. 文中的图片来自Catlike coding http://catl ...

  5. 基于屏幕空间的实时全局光照(Real-time Global Illumination Based On Screen Space)

    目录 Reflective Shadow Maps(RSM) RSM 的重要性采样 RSM 的应用与缺陷 Screen Space Ambient Occulsion(SSAO) SSAO Blur ...

  6. Unity3D ShaderLab立方体图的法线渲染

    Unity3D ShaderLab立方体图的法线渲染 某些情况下,我们希望立方体图的材质球上产生法线效果,来更多的表现细节,比如菱形花纹的玻璃,冰块的表面. 在帧数的协调下,我们可以通过input结构 ...

  7. Unity3d 动态加载场景物件与缓存池的使用

    聊聊Unity3d动态加载场景物件那些事儿. 众所周知,在策划或美术设计完游戏场景地图后,一个场景中可能会存在成千上万个小的物件,比如石头,木箱子,油桶,栅栏等等等等,这些物件并不是游戏中的道具,仅仅 ...

  8. three.js引擎基础知识—摄像机、场景及渲染器

    一.three.js采用右手坐标系: x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外,如下图右: 二.3D编程三要素:场景.渲染器.摄像机 1.场景:创建的物品和模型都需放入场景中 threejs ...

  9. unity3D 涂涂乐使用shader实现上色效果

    unity3D 涂涂乐使用shader实现上色效果 之前我博文里面发过一个简单的通过截图方式来实现的模型上色方法,但是那个方法不合适商用,因为你需要对的很准确才可以把贴图完美截取下来,只要你手抖了一下 ...

随机推荐

  1. 20145333《Java程序设计》课程总结

    每周读书笔记链接汇总 第一周学习总结 第二周学习总结 第三周学习总结 第四周学习总结 第五周学习总结 第六周学习总结 第七周学习总结 第八周学习总结 第九周学习总结 第十周学习总结 实验报告链接汇总 ...

  2. README.android

    Default (and possibly architecture dependents) HAL modules go here. libhardware.so eventually should ...

  3. 【vim】几种模式的切换

    很多初学者启动vim后,不知道怎么输入字符:按了半天字母,结果屏幕还是空的. vim和记事本或WORD不一样,不是一打开后就可以输入文字,此时它处于正常模式. vim一共有4个模式: 正常模式 (No ...

  4. Jackson 框架JSON、XML、List、Map直接相互转换

    博客分类: json   参考:http://www.cnblogs.com/hoojo/archive/2011/04/22/2024628.html 在其基础上做了稍微调整 详情见附件 jacks ...

  5. PHP设计模式(四):生成器模式

  6. 爬虫框架Scrapy之CrawlSpiders

    CrawlSpiders 通过下面的命令可以快速创建 CrawlSpider模板 的代码: scrapy genspider -t crawl tencent tencent.com 上一个案例中,我 ...

  7. hbase(一)region

    前言 文章不含源码,只是一些官方资料的整理和个人理解 架构总览 这张图在大街小巷里都能看到,感觉是hbase架构中最详细最清晰的一张,稍微再补充几点. 1) Hlog是低版本hbase术语,现在称为W ...

  8. MU puzzle

    2017-08-06 20:49:38 writer:pprp 三种操作: 1.MUI -> MUIUI 2.MUUU -> MU 3.MUIII -> MUU 分析:有两个操作:将 ...

  9. Apache 配置SSL网站

    1. 申请证书    现在可以在阿里云或七牛上申请免费的证书,这里以阿里云为例        进入阿里云证书申请界面    https://www.aliyun.com/product/cas     ...

  10. Linux命令详解-mv

    mv命令是move的缩写,可以用来移动文件或者将文件改名(move (rename) files),是Linux系统下常用的命令,经常用来备份文件或者目录. 1.命令格式: mv [选项] 源文件或目 ...