unity3d shader之God Ray上帝之光

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

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

本文的代码是来自unity圣典中某大神的分享,博主做了小小的改进 链接然后就来做下讲解,共有两个shader,一个负责制造ray,一个负责和原屏幕图像混合,于原屏幕图像混合很简单,就是单纯的把两个图像的颜色叠加,控制一下ray的权重,接下来我们着重讲解一下,制造ray的shader是一个fragement shader共有4个外部变量_ScreenLightPos屏幕上光线的位置,这个需要在c#脚本中计算并传出,稍后会讲解_Density密度_Decay衰减_Exposure曝光,用来控制亮度,大家都知道,在相机中,曝光时间越长图像越亮先看vertex shaderv2f 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 blocksCGINCLUDE#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;}ENDCGSubshader{Tags{ "Queue" = "Transparent" }Pass{ZWrite OffBindChannels{Bind "Vertex", vertexBind "texcoord", texcoord0Bind "texcoord1", texcoord1}Fog{ Mode off }CGPROGRAM#pragma fragmentoption ARB_precision_hint_fastest #pragma vertex vert#pragma fragment fragENDCG}}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 rayif (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);

代码如下:

歌里唱的是“你离开我,就是旅行的意义”,

unity3d shader之God Ray上帝之光

相关文章:

你感兴趣的文章:

标签云: