又是一个post-process后期效果,god ray 上帝之光,说起上帝之光就是咱们再看太阳时太阳周围一圈的针状光芒
先放组效果,本文的场景资源均来自浅墨大神,效果为本文shader效果

加入了前篇HDR和Bloom,效果大增:链接

本文的代码是来自unity圣典中某大神的分享,博主做了小小的改进 链接
然后就来做下讲解,共有两个shader,一个负责制造ray,一个负责和原屏幕图像混合,于原屏幕图像混合很简单,就是单纯的把两个图像的颜色叠加,控制一下ray的权重,
接下来我们着重讲解一下,制造ray的shader
是一个fragement shader
共有4个外部变量
_ScreenLightPos屏幕上光线的位置,这个需要在c#脚本中计算并传出,稍后会讲解
_Density密度
_Decay衰减
_Exposure曝光,用来控制亮度,大家都知道,在相机中,曝光时间越长图像越亮

先看vertex shader

	v2f vert(v2in v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); half2 texCoord = v.texcoord;
half2 deltaTexCoord = texCoord - _ScreenLightPos.xy;
deltaTexCoord *= 1.0f / 8 * _Density; texCoord -= deltaTexCoord;
o.uv0 = texCoord;
texCoord -= deltaTexCoord;
o.uv1 = texCoord;
texCoord -= deltaTexCoord;
o.uv2 = texCoord;
texCoord -= deltaTexCoord;
o.uv3 = texCoord;
texCoord -= deltaTexCoord;
o.uv4 = texCoord;
texCoord -= deltaTexCoord;
o.uv5 = texCoord;
texCoord -= deltaTexCoord;
o.uv6 = texCoord;
texCoord -= deltaTexCoord;
o.uv7 = texCoord;
return o;
}

v.texcoord为当前点的坐标

deltaTexCoord为当前点对光源点的反向向量,长度为两点间距离

密度越大deltaTexCoord越大,不超过8,deltaTexCoord始终是个分数
第一个采样点为此处本来位置
采样点渐渐接进光源处
_Density越大采样点间距越大
从0到7,点的位置从光源处越来越近,离此处点越来越远
看看我们的v2f结构体,存了多少坐标点

	struct v2f {
float4 pos : POSITION;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
float2 uv2 : TEXCOORD2;
float2 uv3 : TEXCOORD3;
float2 uv4 : TEXCOORD4;
float2 uv5 : TEXCOORD5;
float2 uv6 : TEXCOORD6;
float2 uv7 : TEXCOORD7;
};

传入值的结构体v2in

	struct v2in {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};

我们就得到了当前点到光源点的一条直线中的八个点的坐标,为fragement shader取色混色用
当然本步骤也可在fragement shader中完成,但效率没有vertex shader好,因为不用每个像素都取样,只是每个顶点取样就好

再看fragement shader

    half4 frag(v2f i) : COLOR
{
half illuminationDecay = 1.0f; half4 color = tex2D(_MainTex, i.uv0)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv1)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv2)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv3)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv4)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv5)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv6)*illuminationDecay;
illuminationDecay *= _Decay;
color += tex2D(_MainTex, i.uv7)*illuminationDecay; color /= 8; return half4(color.xyz * _Exposure, 1); }

illuminationDecay光照衰减,_Decay是我们外部可控衰减

_Exposure增加亮度

调整比重离此处像素点越远也就是离光源越近越衰减,可能有人会问,为什么会这样?因为我们还是要保留大部分为此处点的颜色,如果其他像素权重过大,则会造成此处点颜色不准确,甚至不好的模糊效果。
然后就是混色,基本上的原理就是从光源处打出无数条射线,嗯,可以这么理解。

Ray我们就制造好了,接下来我们需要把光线ray与原屏幕图像混合,这一步就比较简单了,只给出源代码,各位自己意会。

Shader "Custom/god ray 2 blend" {
Properties{
_MainTex("Base (RGB)", 2D) = "" {}
_GodRayTex ("God (RGB)", 2D) = ""{}
_Alpha("_Alpha", Float) = 0.5
} // Shader code pasted into all further CGPROGRAM blocks
CGINCLUDE #include "UnityCG.cginc" struct v2in {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
}; struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
}; sampler2D _MainTex; sampler2D _GodRayTex; uniform float _Alpha; v2f vert(v2in v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
return o;
} half4 frag(v2f i) : COLOR
{
half4 color = tex2D(_MainTex, i.uv) + tex2D(_GodRayTex, i.uv)*_Alpha;
//half4 color = tex2D(_MainTex, i.uv); return color;
} ENDCG Subshader{ Tags{ "Queue" = "Transparent" } Pass{
ZWrite Off BindChannels
{
Bind "Vertex", vertex
Bind "texcoord", texcoord0
Bind "texcoord1", texcoord1
} Fog{ Mode off }
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
ENDCG
} } Fallback off } // shader

然后就是最后一步,也是十分重要的一步就是通过脚本把它弄到屏幕上,
此处的要点就是要求出光源在屏幕中的位置,
Camera类中有这么一个函数可以把世界坐标转换为屏幕坐标
Camera.WorldToScreenPoint(position)
官网介绍如下
Transforms position from world space into screen space.
把position从世界坐标转换为屏幕坐标
Screenspace
is defined in pixels. The bottom-left of the screen is (0,0); the
right-top is (pixelWidth,pixelHeight). The z position is in world units
from the camera.
左下角是屏幕坐标系的原点,右上角是屏幕的最大范围,超出这个范围的光源我们都不进行god ray渲染了,以此作为判断,否则就会进行错误渲染,屏幕超出光照范围了仍在闪烁。

我们把光源的transport传入脚本,然后检验光源的position

外还有重要一点就是判断光源在相机前面还是在后面,如果只判断是否在屏幕内的话,相机转到光源后面也会被渲染god
ray,解决方法在此,WorldToScreenPoint返回的z值为世界空间内光源与相机的距离,为矢量,所以我们就能用z值正负来判断前后了,为
正则光源在相机前可渲染god ray,为负则光源在相机后不可渲染god ray
if (lightScreenPos.z > 0
&& lightScreenPos.x > 0 && lightScreenPos.x <
camera.pixelWidth  && lightScreenPos.y >0 &&
lightScreenPos.y < camera.pixelHeight)
 
其实就这么渲染也可以,但是效果并不好,god ray变成了“god point”,原因刚才分析的,shader的原理是取点到光源的八个点,那渲染的结果也就是出现了好多点,层次很分明,就是因为之混乱和了那8次,解决方式就是多次渲染,点多了,就变成线了
我们要想使效果更好一点就要多次渲染
建立两个renderTexure tempRtA和tempRtB用来互相传值

Graphics.Blit(sourceTexture, tempRtA, material);
第一次过滤结果存在tempRtA
传到下一次渲染做_MainTex
                Graphics.Blit(tempRtA, tempRtB, material);
再传出tempRtB到第三次渲染,再传出tempRtA。。。
                Graphics.Blit(tempRtB, tempRtA, material);
                Graphics.Blit(tempRtA, tempRtB, material);
                Graphics.Blit(tempRtB, tempRtA, material);
最后做混合,把ray texture传到blend shader作为GodRayTex。然后得到最终结果
                materialBlend.SetTexture("_GodRayTex", tempRtA);
                Graphics.Blit(sourceTexture, destTexture, materialBlend, 0);

代码如下:

using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class godRay2 : MonoBehaviour
{
public Transform lightpos;
public Shader curShader;
public Shader curShaderblend;
private Material curMaterial;
private Material curMateriaBlend;
public Vector4 ScreenLightPos = new Vector4(0, 0, 0, 0);
public float Density = 0.01f;
public float Decay = 0.5f;
public float Exposure = 0.5f;
public float Alpha = 1;
public RenderTexture tempRtA = null;
public RenderTexture tempRtB = null; private Vector3 lightScreenPos;
#region Properties
Material material
{
get
{
if (curMaterial == null)
{
curMaterial = new Material(curShader);
curMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return curMaterial;
}
}
Material materialBlend
{
get
{
if (curMateriaBlend == null)
{
curMateriaBlend = new Material(curShaderblend);
curMateriaBlend.hideFlags = HideFlags.HideAndDontSave;
}
return curMateriaBlend;
}
}
#endregion void Start()
{
if (!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
} if (!curShader && !curShader.isSupported)
{
enabled = false;
}
} void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
{ if (curShader != null)
{
lightScreenPos = Camera.main.WorldToScreenPoint(lightpos.position); if (lightScreenPos.z > 0 && lightScreenPos.x > 0 && lightScreenPos.x < camera.pixelWidth && lightScreenPos.y > 0 && lightScreenPos.y < camera.pixelHeight)
{
material.SetVector("ScreenLightPos", new Vector4(lightScreenPos.x / camera.pixelWidth, lightScreenPos.y / camera.pixelHeight, 0, 0));
// material.SetVector("ScreenLightPos", ScreenLightPos);
material.SetFloat("Density", Density);
material.SetFloat("Decay", Decay);
material.SetFloat("Exposure", Exposure);
materialBlend.SetFloat("Alpha", Alpha);
CreateBuffers();
Graphics.Blit(sourceTexture, tempRtA, material);
Graphics.Blit(tempRtA, tempRtB, material);
Graphics.Blit(tempRtB, tempRtA, material);
Graphics.Blit(tempRtA, tempRtB, material);
Graphics.Blit(tempRtB, tempRtA, material); materialBlend.SetTexture("_GodRayTex", tempRtA);
Graphics.Blit(sourceTexture, destTexture, materialBlend, 0);
// Graphics.Blit(tempRtA, destTexture, material, 0);
}
else
{
Graphics.Blit(sourceTexture, destTexture);
}
}
else
{
Graphics.Blit(sourceTexture, destTexture);
} } void CreateBuffers()
{
if (!tempRtA)
{
tempRtA = new RenderTexture(Screen.width / 4, Screen.height / 4, 0);
tempRtA.hideFlags = HideFlags.DontSave;
} if (!tempRtB)
{
tempRtB = new RenderTexture(Screen.width / 4, Screen.height / 4, 0);
tempRtB.hideFlags = HideFlags.DontSave;
}
}
void OnDisable()
{
if (curMaterial)
{
DestroyImmediate(curMaterial);
}
}
}

本shader有几个缺点,在比较暗的场景不要使用,因为光源处不亮,所以效果不好,Ray的质量不高,从例子就可以看出来,Ray很不清晰,此处可以和Unity ImageEffect的Sun shafts作比较

最后放上两组效果

林中闪耀的光芒

   ------ by  wolf96

unity3d shader之God Ray上帝之光的更多相关文章

  1. 【浅墨Unity3D Shader编程】之一 夏威夷篇:游戏场景的创建 & 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨)  ...

  2. 【浅墨Unity3D Shader编程】之中的一个 夏威夷篇:游戏场景的创建 &amp; 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨)  ...

  3. 【译】Unity3D Shader 新手教程(1/6)

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 刚开始接触Unity3D Shader编程时,你会发现有关shader的文档相当散,这也造成初学者对Unity3D ...

  4. Unity3D shader简介

    Unity3D shader简介 可以肯定的说Unity3D使得很多开发者开发游戏更容易.毫无疑问,shader(着色器)编码,仍有很长的路要走.shader是一个专门运行在GPU的程序,经常被神秘包 ...

  5. 转 猫都能学会的Unity3D Shader入门指南(二)

    猫都能学会的Unity3D Shader入门指南(二) 关于本系列 这是Unity3D Shader入门指南系列的第二篇,本系列面向的对象是新接触Shader开发的Unity3D使用者,因为我本身自己 ...

  6. Unity3D Shader入门指南(二)

    关于本系列 这是Unity3D Shader入门指南系列的第二篇,本系列面向的对象是新接触Shader开发的Unity3D使用者,因为我本身自己也是Shader初学者,因此可能会存在错误或者疏漏,如果 ...

  7. Unity3d Shader

    Unity3d Shader 预览Surface Shader主要用来实现光照相关处理,可能更简洁. Vertex and Fragment Shader 如果不与光照交互, 则可以用这个shader ...

  8. 【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&amp;颜色、光照与材质

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40955607 作者:毛星云(浅墨)  ...

  9. 【淡墨Unity3D Shader计划】四 热带雨林的文章: 排除、深度测试、Alpha测试和基本雾编译

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1222/163.html 作者:毛星云 ...

随机推荐

  1. Android - 向服务器发送数据(GET).

    在此,使用HTTP协议,通过GET请求,向服务器发送请求,这种方式适合于数据量小,数据安全性要求不高的情况下. 一,服务器端,使用Servlet. 在服务器端,定义一个HttpServlet的子类,以 ...

  2. 如何在OpenWRT环境下做开发

    1.搭建开发环境 首先,在执行make menuconfig后,会出现下图: 其中,图中红框部分是我定制路由器的系统版本,大家可以根据不同的路由器进行不同的选择:绿框部分表示我们需要编译一个SDK开发 ...

  3. 详细介绍Linux shell脚本基础学习

    Linux shell脚本基础学习这里我们先来第一讲,介绍shell的语法基础,开头.注释.变量和 环境变量,向大家做一个基础的介绍,虽然不涉及具体东西,但是打好基础是以后学习轻松地前提.1. Lin ...

  4. IE str.trim() 不兼容问题解决方法

    本文实例分析了javascript在IE下trim函数无法使用的解决方法: 首先,javascript的trim函数在firefox或者chrome下面使用没有问题: 1 2 3 4 5 <sc ...

  5. Angularjs总结(三)摸态框的使用

    静态页面: <input class="btn btnStyle " value="提 取" type="button" ng-cli ...

  6. js小分享

    之前实现一些js代码时,总觉得无法下手,所以最近在学习一下特别细的知识点,分享笔记.嘻嘻,偷个小懒,我把自己的笔记拍个照片就不打字了.嘎嘎,放心放心,自觉得字写的还算ok的啦- 表示家里的老弟玩游戏, ...

  7. mysql建表出现Timestamp错误

    mysql建表时如果有两个或以上的字段为Timestamp,那么可能会出现如下错误: Incorrect table definition; there can be only one TIMESTA ...

  8. Linux 安装配置 JDK 8

    所需软件包, 可以到Oracle官网去下载,  放到/usr/local/src文件夹下: jdk-8u45-linux-x64.tar.gz 安装: cd /usr/local/srctar -zx ...

  9. SQL70001: This statement is not recognized in this context.

    关于错误: SQL70001: This statement is not recognized in this context. 的产生原因以及解决办法.   在SQL Server Databas ...

  10. XPATH 注入的介绍与代码防御

    0x01 介绍 软件未正确对 XML 中使用的特殊元素进行无害化处理,导致攻击者能够在终端系统处理 XML 的语法.内容或命令之前对其进行修改.在 XML 中,特殊元素可能包括保留字或字符,例如“&l ...