前几天,家里出了一些问题,搞得心情很不好,面试我也取消了。
唉,反正那个伤心啊,不过,昨天处理好了。
所以说啊,家和万事兴。
加油加油!!!
所以心情好了,我又写博客了。
另外说一下:图形我今年2019.5才开始正式学习。
所以每个shader效果有些实现方法可能不是很好的,欢迎大神多多指点。
进入主题吧:
今天实现:Unity Shader - 实现类似镜面反射、水面扰动效果
平面的过渡效果,可参考我之前写的一个,模型按平面渡效果:Unity Shader - 模型过滤效果
Noise 噪点实现类型水面可参考我之前写的:Unity Shader - Noise 噪点应用,实现类似流水的表面效果
模板缓存来只绘制镜面的内容:Unity Shader - 模板简单测试 - 实现镂空/遮罩、描边
上面两个效果组合,就差不多可以得到上面的效果了。
两个pass现实
将模型的顶点按传进来的平面的法线、任一点,来对本体模型镜面顶点
这样再去绘制,就看起来又镜面的效果了
同时我们在镜像体绘制时,添加了纹理柏林噪点偏移UV,看起来有点像水流效果
但我们的镜面体的显示只能在那个地板模型中才绘制,所以我们给地板shader添加了Stencil处理(Stencil的功能实例看上面链接)
镜面详细讲解一下:
绘制模型本体
但需要处理,上面Unity Shader - 模型过滤效果 的过渡效果,因为超过镜面底下,我们就不显示了(可以按需求来处理)
然后再对纹理UV扰动想水流似的,基本就这样就完了
// jave.lin 2019.08.15
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UpdateProps : MonoBehaviour
{
private static int nHash;
private static int pHash;
public GameObject obj;
public GameObject plane;
private Material objMat;
// Start is called before the first frame update
void Start()
{
objMat = obj.GetComponent<MeshRenderer>().sharedMaterial;
nHash = Shader.PropertyToID("n");
pHash = Shader.PropertyToID("p");
}
// Update is called once per frame
void Update()
{
// n==normal of plane
// p==position of plane
var n = plane.transform.up;
var p = plane.transform.position;
objMat.SetVector(nHash, n);
objMat.SetVector(pHash, p);
}
}
// jave.lin 2019.08.15
Shader "Test/MirrorGroundStencil"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "Queue"="Geometry+1" "RenderType"="Opaque" }
Stencil {
Ref 1
Comp Always
Pass Replace
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 col = tex2D(_MainTex, i.uv);
return col * _Color;
}
ENDCG
}
}
}
注释我写的相当详细了
// jave.lin 2019.08.15
Shader "Test/PerlinNoiseMirror" {
Properties {
[NoScaleOffset] _MainTex ("MainTex", 2D) = "white" {} // 主纹理
[NoScaleOffset] _NoiseTex ("NoiseTex", 2D) = "white" {} // 噪点图
_NoiseScaleX ("NoiseScaleX", Range(0, 1)) = 0.1 // 水平噪点放大系数
_NoiseScaleY ("NoiseScaleY", Range(0, 1)) = 0.1 // 垂直放大系数
_NoiseSpeedX ("NoiseSpeedX", Range(0, 10)) = 1 // 水平扰动速度
_NoiseSpeedY ("NoiseSpeedY", Range(0, 10)) = 1 // 垂直扰动速度
_NoiseBrightOffset ("NoiseBrightOffset", Range(0, 0.9)) = 0.25 // 噪点图整体的数值偏移
_NoiseFalloff ("NoiseFalloff", Range(0, 1)) = 1 // 扰动衰减
_MirrorRange ("MirrorRange", Range(0, 3)) = 1 // 镜面范围(最大范围,超出该范围就不反射)
_MirrorAlpha ("MirrorAlpha", Range(0, 1)) = 1 // 镜面图像不透明度
_MirrorFadeAlpha ("_MirrorFadeAlpha", Range(0,1)) = 0.5 // 镜面范围值边缘位置的不透明度,如果调整为0,意思越接近该最大范围的透明就越接近该值:0
}
CGINCLUDE
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 wPos : TEXCOORD1;
};
struct v2f_m {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 normal : TEXCOORD1;
float4 wPos : TEXCOORD2;
};
sampler2D _MainTex;
sampler2D _NoiseTex;
fixed _NoiseScaleX, _NoiseScaleY;
fixed _NoiseSpeedX, _NoiseSpeedY;
fixed _NoiseBrightOffset;
fixed _NoiseFalloff;
float _MirrorRange, _MirrorAlpha, _MirrorFadeAlpha;
float3 n, p; // 镜面法线,镜面任意点
v2f vert_normal (appdata v) {
v2f o;
o.wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag_normal (v2f i) : SV_Target {
float3 dir = i.wPos.xyz - p; // 平面与插值点的指向
half d = dot(dir, n); // 与反向镜面的距离
if (d < 0) discard; // 如果平面背面,那就丢弃
return tex2D(_MainTex, i.uv);
}
v2f_m vert_mirror (appdata v) {
v2f_m o;
o.wPos = mul(unity_ObjectToWorld, v.vertex);
float3 nn = -n; // 法线反向
float3 dp = o.wPos.xyz - p; // 平面点与世界空间的点的向量(即:从平面的点指向世界空间点的方向)
half nd = dot(n, dp); // 计算出点与平面的垂直距离
o.wPos.xyz += nn * (nd * 2); // 将垂直距离反向2倍的距离,就是镜像的位置
o.vertex = mul(unity_MatrixVP, o.wPos);
o.normal.xyz = UnityObjectToWorldNormal(v.normal);
fixed t = nd / _MirrorRange; // 将位置与镜面最大范围比利作为fade alpha的插值系数
fixed a = lerp(_MirrorAlpha, _MirrorAlpha * _MirrorFadeAlpha, t);
o.normal.w = a; // 透明度我们存于o.normal.w
o.wPos.w = nd; // 距离存于o.wPos.w
o.uv = v.uv;
return o;
}
fixed4 frag_mirror (v2f_m i) : SV_Target {
if (i.wPos.w > _MirrorRange) discard; // 超过镜像范围也丢弃
if (i.normal.w <= 0) discard; // 透明度为0丢弃
float3 dir = i.wPos.xyz - p; // 平面与插值点的指向
half d = dot(dir, n); // 与反向镜面的距离
if (d > 0) discard; // 如果超过了平面,那就丢弃
fixed2 ouvxy = fixed2( // 噪点图采样,用于主纹理的UV偏移的
tex2D(_NoiseTex, i.uv + fixed2(_Time.x * _NoiseSpeedX, 0)).r,
tex2D(_NoiseTex, i.uv + fixed2(0, _Time.x * _NoiseSpeedY)).r);
ouvxy -= _NoiseBrightOffset; // 0~1 to ==> -_NoiseBrightOffset~ 1 - _NoiseBrightOffset
ouvxy *= fixed2(_NoiseScaleX, _NoiseScaleY); // 扰动放大系数
float scale = i.wPos.w / _MirrorRange; // 用距离来作为扰动衰减
scale = lerp(scale, 1, (1 - _NoiseFalloff)); // 距离越近扰动越是衰减(即:与镜面距离越近,基本是不扰动的,所以我们可以看到边缘与镜面的像素是吻合的)
ouvxy *= scale;
fixed4 col = tex2D(_MainTex, i.uv + ouvxy); // 加上扰动UV后再采样主纹理
return fixed4(col.rgb, i.normal.w);
}
ENDCG
SubShader {
Tags { "Queue"="Geometry+2" "RenderType"="Opaque" }
Pass {
Cull front
ZTest Always
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Stencil {
Ref 1
Comp Equal
}
CGPROGRAM
#pragma vertex vert_mirror
#pragma fragment frag_mirror
ENDCG
}
Pass {
CGPROGRAM
#pragma vertex vert_normal
#pragma fragment frag_normal
ENDCG
}
}
}
TestShaderToy_NoiseAndMirror 提取码: j1kh 复制这段内容后打开百度网盘手机App,操作更方便哦
打开:PerlinNoiseMirror.unity 场景即可
(今天要去探亲,我就过几天再回来学习shader,然后再博客)