【Unity Shader学习笔记】实现反射与折射模拟水面、使用grabPass与环境贴图

文章目录

  • 写在前面
  • 一个水波效果
    • 大致组成部分与对应的实现方案
    • 交界线与深度贴图
    • 折射效果与GrabPass
    • 使用Cubemap与法线信息来模拟反射
      • 在正确的地点创建对应的cubemap
      • 通过贴图获取法线信息
      • 关于法线贴图
      • 法线空间到世界空间变换
      • 计算反射角
      • 菲涅尔反射
    • 混合所有部分的颜色

写在前面

出于个人写作习惯,还是喜欢在开始之前加上一小段技术无关的叙述,通过这个方式我会更加容易进入到写作状态,当然也可以理解为序章一样的东西,会参杂一些生活上的事,废话会比较多,如果不看的话请直接通过目录跳转。
在完成了这个效果之后,我继续参考了Linden Reid女士的博客希望为我的水面增加上一层折射效果,显然我走的更远了一些,把反射也加上了,期间也复习了使用环境纹理这个知识点,多少算是有一点点在进步吧,当初看书时一知半解的东西随着要解决实际问题,也变得更加清晰了。
今天因为个人身体原因需要会去一趟医院,因此就不对原理进行过深描述。而是转而直接介绍代码的实现,当然了过程中如果有所提及,还是会尽量解释的。
至于涉及的原理,以及过程中必然会涉及到的知识点,其内容肯定是多于我在代码中使用到的,而为了帮助我自己记忆和学习,我也会找时间写下来的,主要参考还是冯乐乐的书,当然官网的文档也会使用。(大概率明天就会写)
接下来就开始吧。

一个水波效果

大致组成部分与对应的实现方案

为了更加明确思路,而不是愣在那里想“我要写个水面”,然后毫无头绪,我们还是把水面的效果拆解开来,并一个一个针对实现吧。

交界线与深度贴图

首先是水面在与其他物体接触的时候会产生的交界线,会与周围有些许不同的效果,这个效果已经在这篇博客里头具体实现过了,当时的效果如下:
但是可以看到这个水面现在空有交界线和边界的起伏,非常简单。

折射效果与GrabPass

折射效果其实简单考虑起来,就是希望让水面下的画面能够进行一部分的扭动。
这里Unity提供了grabPass,通过简单的声明,能够获取当前摄像机渲染的屏幕画面纹理,而通过对这个纹理进行带变形的采样之后再显示出来,就能够实现折射效果。
为了达到这一点,需要对shader代码进行如下设置:

Shader "Custom/Water"
{
   
	Properties{
   
	//some properties here
		...
	}
		SubShader
	{
   
		Tags{
   "RenderType" = "Opaque" "Queue"="Transparent"}//将物体设置为不透明,渲染队列设置为透明队列
		GrabPass{
   "_GrabPass"}//紧接着就声明GrabPass,可以理解为就是一个Pass,将屏幕纹理输出到了指定名字的纹理中
	Pass
	{
   
		CGPROGRAM
		...
			sampler2D _GrabPass;;//在需要的地方声明变量,这里变量名要和GrabPass里的字符串一致
			float4 _GrabPass_TexelSize;//通过后缀获取该图像的纹素大小,若屏幕纹理大小为800*600,
			//那么该变量的xy即为1/800,1/600
		...
		ENDCG
	  } 
		
	}
}

这里解释一下渲染标签。实际上这里渲染的是不透明效果,我们要输出的颜色是grabpass获得的颜色与其他效果的叠加,并不会与背后的其他物体进行混合,因此选择RenderType为Opaque即可,当然也可以选择Transparent然后大概Blend选项,但是没有必要,因为我们已经能通过grabpass某种程度上获得屏幕上的像素了。
渲染队列则必须设置为Transparent,因为这样保证我们当前绘制的物体是在Geometry中的物体之后绘制的,也就意味着在我们获取grabpass图像时,前提必须是其他不透明物体已经绘制完了,这样获得的grabpass才能够包含正确的我们需要的信息。
这部分所需代码与效果:

Shader "Custom/Water"
{
   
	Properties{
   
	//这部分会用到的变量,主要是后两个
	_Color("Color",Color) = (1,1,1,1)
	_DepthFactor("DepthFactor",range(0,1)) =0.5
	_Distortion("_Distortion",float) = 1.0
	_NoiseTex("noiseSample",2D) = "white"{
   }
	}
		SubShader
	{
   
		Tags{
   "RenderType" = "Opaque" "Queue"="Transparent"}
	GrabPass{
   "_GrabPass"}//使用GrabPass
	Pass
	{
   
	CGPROGRAM
	// required to use ComputeScreenPos()
	#include "UnityCG.cginc"

	#pragma vertex vert
	#pragma fragment frag

	 sampler2D _CameraDepthTexture;
	fixed4 _Color;
	float _DepthFactor;
	fixed _Distortion;
	sampler2D _NoiseTex;
	float4 _NoiseTex_ST;
	sampler2D _GrabPass;//通过声明获取需要的Grabpass图像
	float4 _GrabPass_TexelSize;

	struct vertexInput
	 {
   
	   float4 vertex : POSITION;
	   float4 texcoord:TEXCOORD;
	   float3 normal:NORMAL;
	   float4 tangent:TANGENT;
	 };

	struct vertexOutput
	 {
   
	   float4 pos : SV_POSITION;
	   float4 grabScreenPos:texcoord2;
	   float4 UV:TEXCOORD3;
	   float4 originalPos:TEXCOORD4;
	 };

	vertexOutput vert(vertexInput input)
	  {
   
		vertexOutput output;
		output.originalPos= UnityObjectToClipPos(input.vertex);
		output.UV.xy = TRANSFORM_TEX(input.texcoord.xy, _NoiseTex);
		//Animating
		//tex2D(_NoiseTex,input.texcoord)is not available in vertex shader 
		//for there are no UV derivatives in Vertex Shader
		float noise =tex2Dlod(_NoiseTex, float4(output.UV.xy, 0, 0));
		input.vertex.y += cos(_Time.y*10 * noise)*0.2*noise;

		// convert obj-space position to camera clip space
		output.pos = UnityObjectToClipPos(input.vertex);
		//使用了变换前的坐标来获取屏幕坐标,确保纹理会跟着定点动画波动
		output.grabScreenPos = ComputeGrabScreenPos(output.originalPos);


		return output;
	  }
	  float4 frag(vertexOutput input) : COLOR
	  {
   

		  //完成折射形变部分
			  //获取对GrabPass的采样坐标,坐标包含形变,形变从噪声获取
			  //使用噪声来进行偏移,包含了时间变量以让折射存在动感
			  float noise = tex2D(_NoiseTex, input.UV.xy + float2(_Time.x,_Time.x));
			  //bump参数乘以纹素大小以得到正确的UV偏移量
			  float2 bump = float2(noise * 

你可能感兴趣的:(【Unity Shader学习笔记】实现反射与折射模拟水面、使用grabPass与环境贴图)