Ta痕迹(10)——啃书之《Unity Shader入门精要》:非真实感 ...
1.第十四章概述非真实感渲染(Non-Photorealistic Rendering NPR)的主要目标是,使用一些渲染方法使得画面达到和某些特殊的绘画风格相似的效果,例如卡通风格、水彩风格等。
2.简单的卡通风格渲染
卡通风格是一种常见的渲染风格,其共同特点为:黑色的线条描边,界限分明的明暗变化。
所以,要实现一个简单的卡通风格渲染,我们要做两步,一是为物体描边,二是使用不同的光照模型。
2.1渲染轮廓线
渲染轮廓线的方法大致可以分为五种:
[*]基于观察角度和表面法线的轮廓线渲染。即使用视角方向和表面法线的点乘结果得到轮廓线信息,这个方法简单快速,但效果不好。
[*]过程式几何轮廓线渲染。先渲染一遍模型背面,并使用某些技术得到其轮廓线,再渲染一遍正面。这种方法快速有效,但是不适合类似立方体这样平整的模型。
[*]基于图像处理的轮廓线渲染。略
[*]基于轮廓边检测的轮廓线渲染。上面提到的方法皆无法控制轮廓线的风格渲染,这显然不行。为此,我们需要精准地确定轮廓线,然后直接渲染他们。
[*]最后一种方法就是想办法组合上述几种方法。
2.2插播一些问题
对于轮廓边检测的公式,书里是这样给出的
仔细想想,两个三角形一个朝正面一个朝背面,它们的法线和视角方向点乘,应该一个是正,一个是负才对。不过没找到有人care这个问题,我也不是很自信。
另一个问题是过程式几何不适合立方体这样的平整模型。
书的作者的博客里有更详细的描述:
像cube这样的模型,它的同一个顶点在不同面上具有不同的顶点法向,所以向外扩张后会形成一个gaps。然后去Unity上试了一下,结果是这样的:
一个很奇怪的现象,按理来说,一个立方体应该只有8个顶点,但是这样的渲染结果就好像把背面的顶点分裂了一样,于是继续搜寻答案。
在这个问题下面找到了答案:
一个很严重的误解就是,一个立方体不止有8个顶点,由三角形面片渲染的立方体,至少也要有24个顶点(一个面4个),这个就纯属于忘了。
另一个问题就像上面的回答,在立方体顶点的这种情况下,不是一个顶点有很多法向量,而是有不同法向量的顶点重合在了一起,当然,如果有需求的话,也可以让这些法向量相加,得到一个唯一的法向量。
2.3本书使用的渲染方法
本书使用的是过程几何式渲染,获得轮廓线的方式则是将整个背面渲染一边,然后让他们沿各自的法线向外扩张一定距离。
这一方法相当于“增厚”了背面模型,像是在模型外添上一层壳,所以又叫Shell method
也正因如此,如果这层“壳”太厚,就可能遮挡住正面的模型,就像下面这样
得,成哆啦A 了
所以,在扩张背面之前,我们把顶点的法线z值做一定的修改,比如
但这无法完全解决问题,这个值过大的话并且还会造成背面的偏移。
但是,正如作者博客中的话,
但它一个非常吸引人的优点就是,不需要任何关于相邻顶点/边等信息,所有的处理都是独立的,因此从速度上来说很快2.4光照的处理
在初见纹理的章节,其实已经部分实现了卡通风格渲染的光照效果,就是渐变纹理那一节。
这里作者给出了一个更正式的名字:基于色调的着色技术(tone-based shading)。
在实现中,我们往往会使用漫反射系数对一张一维纹理进行采样,以控制漫反射色调这当然不够,对于高光部分,我们也要进行处理。
Blinn-Phong的高光计算是这样的
就和漫反射的处理方式一样,我们让Normal和halfDir的点积和一个阈值比较(漫反射那里只不过用一张纹理代替了这个阈值),当点积大于这个阈值,我们就返回1,否则返回0。
这比漫反射的处理更简单,也丢失了更多细节,尤其是0和1跳变处的像素,这会让高光边缘的锯齿很明显。
书中给出了一种解决办法
其中W值的选定似乎还有说法,粗略探究一下,觉得以后还会遇到,就先放在这。
2.5贴一贴代码
Shader "Unity Shaders Book/Chapter 14/Toon Shading" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Outline ("Outline", Range(0, 1)) = 0.1
_OutlineColor ("Outline Color", Color) = (0, 0, 0, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_SpecularScale ("Specular Scale", Range(0, 0.1)) = 0.01
}
SubShader {
//渲染方式的设置往往是一个要考虑的问题
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
//渲染轮廓线的Pass有一定的泛用性
NAME "OUTLINE"
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert (a2v v) {
v2f o;
float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
normal.z = -2;
pos = pos + float4(normalize(normal), 0) * _Outline;
o.pos = mul(UNITY_MATRIX_P, pos);
return o;
}
float4 frag(v2f i) : SV_Target {
return float4(_OutlineColor.rgb, 1);
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _Ramp;
fixed4 _Specular;
fixed _SpecularScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};
struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
SHADOW_COORDS(3)
};
v2f vert (a2v v) {
v2f o;
o.pos = UnityObjectToClipPos( v.vertex);
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
o.worldNormal= UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
float4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir);
fixed4 c = tex2D (_MainTex, i.uv);
fixed3 albedo = c.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed diff =dot(worldNormal, worldLightDir);
diff = (diff * 0.5 + 0.5) * atten;
fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb;
fixed spec = dot(worldNormal, worldHalfDir);
fixed w = fwidth(spec) * 2.0;
fixed3 specular = _Specular.rgb * lerp(0, 1, smoothstep(-w, w, spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
3.素描风格渲染
素描风格渲染原理基础同样是基于色调的着色技术,但问题在于它使用的纹理应该是由密至疏的线条,恕我现在还不懂UV展开的具体原理,所以还没法子想通这一节。
贴贴代码算了,开摆!等我以后修得正果,妖孽自然现行。
Shader "Unity Shaders Book/Chapter 14/Hatching" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_TileFactor ("Tile Factor", Float) = 1
_Outline ("Outline", Range(0, 1)) = 0.1
_Hatch0 ("Hatch 0", 2D) = "white" {}
_Hatch1 ("Hatch 1", 2D) = "white" {}
_Hatch2 ("Hatch 2", 2D) = "white" {}
_Hatch3 ("Hatch 3", 2D) = "white" {}
_Hatch4 ("Hatch 4", 2D) = "white" {}
_Hatch5 ("Hatch 5", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
UsePass "Unity Shaders Book/Chapter 14/Toon Shading/OUTLINE"
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
fixed4 _Color;
float _TileFactor;
sampler2D _Hatch0;
sampler2D _Hatch1;
sampler2D _Hatch2;
sampler2D _Hatch3;
sampler2D _Hatch4;
sampler2D _Hatch5;
struct a2v {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
fixed3 hatchWeights0 : TEXCOORD1;
fixed3 hatchWeights1 : TEXCOORD2;
float3 worldPos : TEXCOORD3;
SHADOW_COORDS(4)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//TileFactor可以放大纹理坐标,让一定范围内的像素能采样比原来更大的排线纹理
//可以认为原来一张排线图贴到模型上,现在是吧很多张排线图贴上去,
//线条就会更加细密,更加好看
o.uv = v.texcoord.xy * _TileFactor;
fixed3 worldLightDir = normalize(WorldSpaceLightDir(v.vertex));
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed diff = max(0, dot(worldLightDir, worldNormal));
o.hatchWeights0 = fixed3(0, 0, 0);
o.hatchWeights1 = fixed3(0, 0, 0);
float hatchFactor = diff * 7.0;
//一下这一番骚操作初看可以更加顺滑地衔接五张图,其实视觉效果..没有很明显的变化
//如果能把这五张纹理连接起来,应该就不用这番操作,之间diffuse坐标完事
//但为了能实现细密的线条,所以不得不把纹理分开来
//所以其实就是一个采样的问题?但还是得想通UV啊
//淦
if (hatchFactor > 6.0) {
// Pure white, do nothing
} else if (hatchFactor > 5.0) {
o.hatchWeights0.x = hatchFactor - 5.0;
} else if (hatchFactor > 4.0) {
o.hatchWeights0.x = hatchFactor - 4.0;
o.hatchWeights0.y = 1.0 - o.hatchWeights0.x;
} else if (hatchFactor > 3.0) {
o.hatchWeights0.y = hatchFactor - 3.0;
o.hatchWeights0.z = 1.0 - o.hatchWeights0.y;
} else if (hatchFactor > 2.0) {
o.hatchWeights0.z = hatchFactor - 2.0;
o.hatchWeights1.x = 1.0 - o.hatchWeights0.z;
} else if (hatchFactor > 1.0) {
o.hatchWeights1.x = hatchFactor - 1.0;
o.hatchWeights1.y = 1.0 - o.hatchWeights1.x;
} else {
o.hatchWeights1.y = hatchFactor;
o.hatchWeights1.z = 1.0 - o.hatchWeights1.y;
}
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 hatchTex0 = tex2D(_Hatch0, i.uv) * i.hatchWeights0.x;
fixed4 hatchTex1 = tex2D(_Hatch1, i.uv) * i.hatchWeights0.y;
fixed4 hatchTex2 = tex2D(_Hatch2, i.uv) * i.hatchWeights0.z;
fixed4 hatchTex3 = tex2D(_Hatch3, i.uv) * i.hatchWeights1.x;
fixed4 hatchTex4 = tex2D(_Hatch4, i.uv) * i.hatchWeights1.y;
fixed4 hatchTex5 = tex2D(_Hatch5, i.uv) * i.hatchWeights1.z;
fixed4 whiteColor = fixed4(1, 1, 1, 1) * (1 - i.hatchWeights0.x - i.hatchWeights0.y - i.hatchWeights0.z -
i.hatchWeights1.x - i.hatchWeights1.y - i.hatchWeights1.z);
fixed4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5 + whiteColor;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(hatchColor.rgb * _Color.rgb * atten, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}4.噪声概述
噪声纹理就是程序化生成的伪随机纹理,用以解决GPU难以生成随机数的问题。使用噪声,我们可以更好地模拟一些非常好看的效果。
回头看来,这一节的所有内容都是之前的某个技术+噪声纹理应用,来得到"进阶"效果
消融效果是通过噪声纹理采样控制透明度测试技术中的阈值
水波效果是通过噪声纹理采样控制运用了菲涅尔效果的玻璃效果中的法线
飘渺的雾效是是通过噪声纹理采样控制了全局雾效中的雾效系数
5.消融(我的评价是:)
本书的简单消融效果原理非常简单,即噪声纹理+透明度测试,我们需要把透明度测试的阈值与噪声纹理的采样结果关联起来就可以了。其实这里不关透明度屁事,只是流程是从透明度测试的流程那里修改而来的。
还是贴一下代码,把要点都写在注释里。
Shader "Unity Shaders Book/Chapter 15/Dissolve" {
Properties {
_BurnAmount ("Burn Amount", Range(0.0, 1.0)) = 0.0
_LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BurnFirstColor("Burn First Color", Color) = (1, 0, 0, 1)
_BurnSecondColor("Burn Second Color", Color) = (1, 0, 0, 1)
_BurnMap("Burn Map", 2D) = "white"{}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Off
CGPROGRAM
#include "Lighting.cginc"
#include "AutoLight.cginc"
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
fixed _BurnAmount;
fixed _LineWidth;
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _BurnFirstColor;
fixed4 _BurnSecondColor;
sampler2D _BurnMap;
float4 _MainTex_ST;
float4 _BumpMap_ST;
float4 _BurnMap_ST;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uvMainTex : TEXCOORD0;
float2 uvBumpMap : TEXCOORD1;
float2 uvBurnMap : TEXCOORD2;
float3 lightDir : TEXCOORD3;
float3 worldPos : TEXCOORD4;
SHADOW_COORDS(5)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
TANGENT_SPACE_ROTATION;
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
//我们利用纹理采样的结果减去一个给定的阈值来控制消融
//Clip函数 当传入的参数小于0时,该片元就会被丢弃
clip(burn.r - _BurnAmount);
float3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
//仅仅简单地丢弃片元产生的视觉效果是不尽如人意的
//所以,引入两个颜色,来让消融阈值在(0,_LineWidth)之间的片元进行给定颜色的线性插值
//让消融的边缘产生视觉效果
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
//和卡通风格渲染结合起来看,smoothstep算是比较重要
//burn.r-_BurnAmount,越大,就离消融边缘越远,所以当其大于_LineWidth时,t = 0
//t值有两个作用
//一是控制消融边缘的颜色插值,我们为边缘设置两个颜色,头尾各一个,这样可以让消融边缘产生
//颜色渐变的效果
//一个是控制物体本色和边缘颜色之间的插值,照理来讲,越远离消融边缘,物体本色的影响因子
//就更大
//越远离边缘 即burn.r - _BurnAmount越大,t越小,这样插值....看最后一行代码
fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
//这个幂乘只是烧焦效果的视觉优化
burnColor = pow(burnColor, 5);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
//step函数的作用是返回(第二个参数>第一个参数),
//书里的意思是保证_BurnAmount为0时不显示任何消融效果
//也就是说怕纹理采样出现负值?好像不是很合理。
fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));
return fixed4(finalColor, 1);
}
ENDCG
}
// Pass to render object as a shadow caster
// 就像透明度测试的阴影问题一样,我们需要再用一个Pass重新计算阴影纹理,来剔除已经消失的那些片
//元的阴影
Pass {
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
fixed _BurnAmount;
sampler2D _BurnMap;
float4 _BurnMap_ST;
struct v2f {
V2F_SHADOW_CASTER;
float2 uvBurnMap : TEXCOORD1;
};
v2f vert(appdata_base v) {
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
}
6.水波效果
正如我在概述中的总结,水波效果的代码看起来很吓人,但其实不过是利用噪声纹理控制法线的菲涅尔光照(玻璃版本,具体体现为物体本身没有颜色,只需要考虑反射和透射)
它看起来吓人的原因很可能是...菲涅尔光照的实现代码本来就很吓人。
Shader "Unity Shaders Book/Chapter 15/Water Wave" {
Properties {
_Color ("Main Color", Color) = (0, 0.15, 0.115, 1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_WaveMap ("Wave Map", 2D) = "bump" {}
_Cubemap ("Envionment Cubemap", Cube) = "_Skybox" {}
_WaveXSpeed ("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01
_WaveYSpeed ("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01
_Distortion ("Distortion", Range(0, 100)) = 10
}
SubShader {
// We must be transparent, so other objects are drawn before this one.
Tags { "Queue"="Transparent" "RenderType"="Opaque" }
// This pass grabs the screen behind the object into a texture.
// We can access the result in the next pass as _RefractionTex
// 抓取渲染纹理是为了获得透射部分的颜色
GrabPass { "_RefractionTex" }
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _WaveMap;
float4 _WaveMap_ST;
samplerCUBE _Cubemap;
fixed _WaveXSpeed;
fixed _WaveYSpeed;
float _Distortion;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
float4 uv : TEXCOORD1;
float4 TtoW0 : TEXCOORD2;
float4 TtoW1 : TEXCOORD3;
float4 TtoW2 : TEXCOORD4;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _WaveMap);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag(v2f i) : SV_Target {
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//Speed用来实现水波的动效
float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
//所以最终噪声影响了两个点
//一个是反射的法线,这导致反射部分的颜色是混乱的,就像是泡沫上流动的五颜六色
//不过学过小学二年级的同学都知道,这和物理原理不沾半点关系
//产生这种现象的原因是水面薄膜厚度不均匀,导致反射光的光程产生差值
//最终由于干涉现象返回不同波长也就是不同颜色的光
//其实都是我已经忘得差不多了,以上就是随便翻了翻书瞎讲了一下
//第二个影响投射的偏移量,这个就是模拟透过水面看物体的扭曲效果,也就是波动效果,这是
//和时间配合产生的效果。
//这个的物理原理我就不清楚了
// Get the normal in tangent space
fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb;
fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb;
fixed3 bump = normalize(bump1 + bump2);
// Compute the offset in tangent space
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
fixed3 refrCol = tex2D( _RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
// Convert the normal to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
fixed3 reflDir = reflect(-viewDir, bump);
fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb * _Color.rgb;
fixed fresnel = pow(1 - saturate(dot(viewDir, bump)), 4);
fixed3 finalColor = reflCol * fresnel+ refrCol * (1 - fresnel);
return fixed4(finalColor, 1);
}
ENDCG
}
}
// Do not cast shadow
FallBack Off
}7.全局雾效
累了,不说废话了。
Shader "Unity Shaders Book/Chapter 15/Fog With Noise" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_FogDensity ("Fog Density", Float) = 1.0
_FogColor ("Fog Color", Color) = (1, 1, 1, 1)
_FogStart ("Fog Start", Float) = 0.0
_FogEnd ("Fog End", Float) = 1.0
_NoiseTex ("Noise Texture", 2D) = "white" {}
_FogXSpeed ("Fog Horizontal Speed", Float) = 0.1
_FogYSpeed ("Fog Vertical Speed", Float) = 0.1
_NoiseAmount ("Noise Amount", Float) = 1
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
float4x4 _FrustumCornersRay;
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
half _FogDensity;
fixed4 _FogColor;
float _FogStart;
float _FogEnd;
sampler2D _NoiseTex;
half _FogXSpeed;
half _FogYSpeed;
half _NoiseAmount;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uv_depth : TEXCOORD1;
float4 interpolatedRay : TEXCOORD2;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv_depth.y = 1 - o.uv_depth.y;
#endif
int index = 0;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
index = 0;
} else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
index = 1;
} else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
index = 2;
} else {
index = 3;
}
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
index = 3 - index;
#endif
o.interpolatedRay = _FrustumCornersRay;
return o;
}
fixed4 frag(v2f i) : SV_Target {
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
float2 speed = _Time.y * float2(_FogXSpeed, _FogYSpeed);
//似乎是把噪声映射到(-0.5,0.5)
//这样导致_NoiseAmount越大,雾的浓度差越大,即浓的地方更浓,淡的地方更淡
float noise = (tex2D(_NoiseTex, i.uv + speed).r - 0.5) * _NoiseAmount;
float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
fogDensity = saturate(fogDensity * _FogDensity * (1 + noise));
fixed4 finalColor = tex2D(_MainTex, i.uv);
finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
return finalColor;
}
ENDCG
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}
页:
[1]