天空盒和水面渲染

天空盒和水面渲染

七月 04, 2022

天空球

防止被相机裁剪

1
2
3
4
5
#if UNITY_REVERSED_Z
o.vertex.z = o.vertex.w * 0.000001f;
#else
o.vertex.z = o.vertex.w * 0.999999f;
#endif

渲染队列改成1000左右。

水面

水面反射

在平面的反射面建立一个摄像头,将渲染的Texture直接采样到平面上达到映射的效果,通过clipPlaneOffset 修正物体在通过改变反射矩阵修改具体位置。
在这里插入图片描述

开抄
PlanarReflection.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;


[ExecuteInEditMode]
public class PlanarReflection : MonoBehaviour {
public LayerMask _reflectionMask = -1;
public bool _reflectSkybox = false;
public float _clipPlaneOffset = 0.07F;
//反射图属性名
const string _reflectionTex = "_ReflectionTex";
Camera _reflectionCamera;
Vector3 _oldpos;
RenderTexture _bluredReflectionTexture;
Material _sharedMaterial;
//模糊效果相关参数
public bool _blurOn = true;
[Range(0.0f, 5.0f)]
public float _blurSize = 1;
[Range(0, 10)]
public int _blurIterations = 2;
[Range(1.0f, 4.0f)]
public float _downsample = 1;
//记录上述模糊参数,用于判断参数是否发生变化
private bool _oldBlurOn;
private float _oldBlurSize;
private int _oldBlurIterations;
private float _oldDownsample;
//模糊shader
private Shader _blurShader;
private Material _blurMaterial;
//用来判断当前是否正在渲染反射图
private static bool _insideRendering;

Material BlurMaterial {
get {
if (_blurMaterial == null) {
_blurMaterial = new Material(_blurShader);
return _blurMaterial;
}
return _blurMaterial;
}
}

void Awake() {
_oldBlurOn = _blurOn;
_oldBlurSize = _blurSize;
_oldBlurIterations = _blurIterations;
_oldDownsample = _downsample;
}

void Start() {
_sharedMaterial = GetComponent<MeshRenderer>().sharedMaterial;
_blurShader = Shader.Find("Hidden/KawaseBlur");
if (_blurShader == null)
Debug.LogError("缺少Hidden/KawaseBlur Shader");
}

bool _blurParamChanged;
void Update()
{
if (_blurParamChanged) {
_oldBlurOn = _blurOn;
_oldBlurSize = _blurSize;
_oldBlurIterations = _blurIterations;
_oldDownsample = _downsample;
}

if (_blurOn != _oldBlurOn || _blurSize != _oldBlurSize || _blurIterations != _oldBlurIterations || _downsample!= _oldDownsample)
{
_blurParamChanged = true;
}
}

//创建反射用的摄像机
Camera CreateReflectionCamera(Camera cam) {
//生成Camera
String reflName = gameObject.name + "Reflection" + cam.name;
GameObject go = new GameObject(reflName);
//go.hideFlags = HideFlags.HideAndDontSave;
go.hideFlags = HideFlags.HideAndDontSave;

Camera reflectCamera = go.AddComponent<Camera>();
//设置反射相机的参数
HoldCameraSettings(reflectCamera);
//创建RT并绑定Camera
if (!reflectCamera.targetTexture) {
reflectCamera.targetTexture = CreateTexture(cam);
}

return reflectCamera;
}
//设置反射相机的参数
void HoldCameraSettings(Camera heplerCam)
{
heplerCam.backgroundColor = Color.black;
heplerCam.clearFlags = _reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
heplerCam.renderingPath = RenderingPath.Forward;
heplerCam.cullingMask = _reflectionMask;
heplerCam.allowMSAA = false;
heplerCam.enabled = false;
}
//创建RT
RenderTexture CreateTexture(Camera sourceCam) {
int width = Mathf.RoundToInt(Screen.width / _downsample);
int height = Mathf.RoundToInt(Screen.height / _downsample);
RenderTextureFormat formatRT = sourceCam.allowHDR ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default;
RenderTexture rt = new RenderTexture(width, height, 24, formatRT);
rt.hideFlags = HideFlags.DontSave;
return rt;
}
//内置回调函数,物体渲染之前会先调用该函数
void OnWillRenderObject() {
Camera currentCam = Camera.current;
if (currentCam == null) {
return;
}

#if !UNITY_EDITOR
if (!currentCam.gameObject.CompareTag("MainCamera"))
return;
#endif

if (_insideRendering) {
return;
}
_insideRendering = true;

if (_reflectionCamera == null) {
_reflectionCamera = CreateReflectionCamera(currentCam);
}

//渲染反射图
RenderReflection(currentCam, _reflectionCamera);

//是否对反射图进行模糊
if (_reflectionCamera && _sharedMaterial) {
if (_blurOn) {
if (_bluredReflectionTexture == null)
_bluredReflectionTexture = CreateTexture(currentCam);
PostProcessTexture(currentCam, _reflectionCamera.targetTexture, _bluredReflectionTexture);
_sharedMaterial.SetTexture(_reflectionTex, _bluredReflectionTexture);
}
else {
_sharedMaterial.SetTexture(_reflectionTex, _reflectionCamera.targetTexture);
}
}

_insideRendering = false;
}

//调用反射相机,渲染反射图
void RenderReflection(Camera currentCam, Camera reflectCamera) {
if (reflectCamera == null) {
Debug.LogError("反射Camera无效");
return;
}
if (_sharedMaterial && !_sharedMaterial.HasProperty(_reflectionTex))
{
Debug.LogError("Shader中缺少_ReflectionTex属性");
return;
}
//保持反射相机的参数
HoldCameraSettings(reflectCamera);

if (_reflectSkybox) {
if (currentCam.gameObject.GetComponent(typeof(Skybox))) {
Skybox sb = (Skybox)reflectCamera.gameObject.GetComponent(typeof(Skybox));
if (!sb) {
sb = (Skybox)reflectCamera.gameObject.AddComponent(typeof(Skybox));
}
sb.material = ((Skybox)currentCam.GetComponent(typeof(Skybox))).material;
}
}

bool isInvertCulling = GL.invertCulling;
GL.invertCulling = true;

Transform reflectiveSurface = this.transform; //waterHeight;

Vector3 eulerA = currentCam.transform.eulerAngles;

reflectCamera.transform.eulerAngles = new Vector3(-eulerA.x, eulerA.y, eulerA.z);
reflectCamera.transform.position = currentCam.transform.position;

Vector3 pos = reflectiveSurface.transform.position;
pos.y = reflectiveSurface.position.y;
Vector3 normal = reflectiveSurface.transform.up;
float d = -Vector3.Dot(normal, pos) - _clipPlaneOffset;
Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);

Matrix4x4 reflection = Matrix4x4.zero;
reflection = CalculateReflectionMatrix(reflection, reflectionPlane);
_oldpos = currentCam.transform.position;
Vector3 newpos = reflection.MultiplyPoint(_oldpos);

reflectCamera.worldToCameraMatrix = currentCam.worldToCameraMatrix * reflection;

Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal, 1.0f);

Matrix4x4 projection = currentCam.projectionMatrix;
projection = CalculateObliqueMatrix(projection, clipPlane);
reflectCamera.projectionMatrix = projection;

reflectCamera.transform.position = newpos;
Vector3 euler = currentCam.transform.eulerAngles;
reflectCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);

reflectCamera.Render();

GL.invertCulling = isInvertCulling;
}

static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane) {
Vector4 q = projection.inverse * new Vector4(
Mathf.Sign(clipPlane.x),
Mathf.Sign(clipPlane.y),
1.0F,
1.0F
);
Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
// third row = clip plane - fourth row
projection[2] = c.x - projection[3];
projection[6] = c.y - projection[7];
projection[10] = c.z - projection[11];
projection[14] = c.w - projection[15];

return projection;
}

static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane) {
reflectionMat.m00 = (1.0F - 2.0F * plane[0] * plane[0]);
reflectionMat.m01 = (-2.0F * plane[0] * plane[1]);
reflectionMat.m02 = (-2.0F * plane[0] * plane[2]);
reflectionMat.m03 = (-2.0F * plane[3] * plane[0]);

reflectionMat.m10 = (-2.0F * plane[1] * plane[0]);
reflectionMat.m11 = (1.0F - 2.0F * plane[1] * plane[1]);
reflectionMat.m12 = (-2.0F * plane[1] * plane[2]);
reflectionMat.m13 = (-2.0F * plane[3] * plane[1]);

reflectionMat.m20 = (-2.0F * plane[2] * plane[0]);
reflectionMat.m21 = (-2.0F * plane[2] * plane[1]);
reflectionMat.m22 = (1.0F - 2.0F * plane[2] * plane[2]);
reflectionMat.m23 = (-2.0F * plane[3] * plane[2]);

reflectionMat.m30 = 0.0F;
reflectionMat.m31 = 0.0F;
reflectionMat.m32 = 0.0F;
reflectionMat.m33 = 1.0F;

return reflectionMat;
}

Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign) {
Vector3 offsetPos = pos + normal * _clipPlaneOffset;
Matrix4x4 m = cam.worldToCameraMatrix;
Vector3 cpos = m.MultiplyPoint(offsetPos);
Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;

return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
}

//对反射图进行图像处理(利用command buffer实现)
private Dictionary<Camera, CommandBuffer> _cameras = new Dictionary<Camera, CommandBuffer>();
void PostProcessTexture(Camera cam, RenderTexture source, RenderTexture dest)
{
//参数有变化需要刷新commandbuffer
if (_blurParamChanged)
{
if (_cameras.ContainsKey(cam))
cam.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, _cameras[cam]);
_cameras.Remove(cam);
}
//已经设置了commandbuffer就不用再执行了
if (_cameras.ContainsKey(cam))
return;

CommandBuffer buf = new CommandBuffer();
buf.name = "Blur Reflection Texture";
_cameras[cam] = buf;
float width = source.width;
float height = source.height;
int rtW = Mathf.RoundToInt(width / _downsample);
int rtH = Mathf.RoundToInt(height / _downsample);

int blurredID = Shader.PropertyToID("_Temp1");
int blurredID2 = Shader.PropertyToID("_Temp2");
buf.GetTemporaryRT(blurredID, rtW, rtH, 0, FilterMode.Bilinear, source.format);
buf.GetTemporaryRT(blurredID2, rtW, rtH, 0, FilterMode.Bilinear, source.format);

buf.Blit((Texture)source, blurredID);
for (int i = 0; i < _blurIterations; i++)
{
float iterationOffs = (i * 1.0f);
buf.SetGlobalFloat("_Offset", iterationOffs / _downsample + _blurSize);
buf.Blit(blurredID, blurredID2, BlurMaterial, 0);
buf.Blit(blurredID2, blurredID, BlurMaterial, 0);
}
buf.Blit(blurredID, dest);

buf.ReleaseTemporaryRT(blurredID);
buf.ReleaseTemporaryRT(blurredID2);

cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, buf);
}

}

水面扰动

在这里插入图片描述
在这里插入图片描述
下面的Vertex pos 是为了降低远处波动。
效果
在这里插入图片描述

代码实现
粗糙用代码还原了一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
Shader "WaterCode"
{
Properties
{
_ReflectionTex("ReflectionTex", 2D) = "white" {}
_WaterNormal("WaterNormal", 2D) = "white" {}
_WaterSpeed1("WaterSpeed1", Float) = 0.1
_NormalTilling("NormalTilling", Float) = 8
_WaterNoise("WaterNoise", Float) = 1
_Vector0("Vector 0", Vector) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog

#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
float3 normal : NORMAL;
};

struct v2f
{
float2 texcoord0 : TEXCOORD0;

float4 pos : SV_POSITION;
float4 screenPos:TEXCOORD1;
float3 worldPos:TEXCOORD2;
float3 worldTangent : TEXCOORD3;
float3 worldNormal : TEXCOORD4;
float3 worldBitangent : TEXCOORD5;
};

sampler2D _ReflectionTex;
float4 _ReflectionTex_ST;
sampler2D _WaterNormal;
float _NormalTilling;
float _WaterSpeed1;
float4 _Vector0;
float _WaterNoise;

v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldTangent = normalize( mul(unity_ObjectToWorld, v.tangent).xyz);
o.worldNormal = normalize( mul(unity_ObjectToWorld, float4(v.normal,0.0)).xyz);
o.worldBitangent = normalize(cross(o.worldNormal,o.worldTangent)*v.tangent.w);
o.screenPos = ComputeScreenPos(o.pos);
o.texcoord0 = v.uv;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
half4 screenPos = i.screenPos;
float3 worldTangent = normalize(i.worldTangent);
float3 worldNormal = normalize(i.worldNormal);
float3 worldBitangent = normalize(i.worldBitangent);
float3 tanToWorld0 = float3( worldTangent.x, worldBitangent.x, worldNormal.x );
float3 tanToWorld1 = float3( worldTangent.y, worldBitangent.y, worldNormal.y );
float3 tanToWorld2 = float3( worldTangent.z, worldBitangent.z, worldNormal.z );
half2 uv1_WaterNormal = i.worldPos.xz/_NormalTilling + _Time*_WaterSpeed1;
half2 uv2_WaterNormal = i.worldPos.xz/_NormalTilling - _Time*_WaterSpeed1;
half3 waterNormal = (tex2D(_WaterNormal,uv1_WaterNormal)+tex2D(_WaterNormal,uv2_WaterNormal)).xyz*0.5f;
waterNormal.z =sqrt( 1.0f- dot( waterNormal.xy,waterNormal.xy));
//水面波动法线
waterNormal = float3(dot(tanToWorld0,waterNormal), dot(tanToWorld1,waterNormal), dot(tanToWorld2,waterNormal));

//screenPos
screenPos = screenPos / screenPos.w;
screenPos.z = ( UNITY_NEAR_CLIP_VALUE >= 0 ) ? screenPos.z : screenPos.z * 0.5 + 0.5;

half2 uv_result_reflection = screenPos.xy+ (waterNormal.xy/ (i.pos.w+1))*_WaterNoise;

// sample the texture
fixed4 col = tex2D(_ReflectionTex,uv_result_reflection);
// apply fog
return col;
}
ENDCG
}
}
}

折射效果

用让水底贴图与水面反射贴图用菲尼尔因子做插值,当角度越垂直,水底越清晰。

折射扭曲

用WaterNoise做他的uv进行扰动,达到效果。

水底高度

给水底做视差贴图的效果,造成视差高度的效果。