26
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Unity】GrabPassを用いた屈折(Refraction)シェーダー

Posted at

Unityのシェーダーで、GrabPassを用いてガラスのような物体を疑似的に表現する方法です。

fast.gif

まず、"Queue"="Transparent"でGrabPassを行うことで、不透明なオブジェクトやスカイボックスをレンダリングした背面をテクスチャ_BackgroundTextureに書き込んでおきます。
フラグメントシェーダーでは組込関数refractを用いて計算した屈折方向に適当にレイを進めてから、_BackgroundTextureからサンプリングすることで屈折を再現しています。

シェーダー内ではSchlickの近似式を用いてフレネル反射係数を計算し、反射光と屈折光をブレンドしていますが、これは屈折係数が小さい場合見た目にあまり影響がないので必要ないかもしれません。

Refraction.Shader
Shader "Refraction"
{
	Properties
	{
		_RefractionIndex("Refraction Index", Range(1.0, 3.0)) = 1.33
		_Distance("Distance", Float) = 10.0
		_Tint("Tint", Color) = (0.8, 0.5, 0.5)
		_SpecularColor ("Specular Color", Color) = (1, 1, 1)
		_Shiness ("Shiness", Float) = 10.0
	}
	SubShader
	{
		Tags {
			"RenderType" = "Transparent"
			"Queue" = "Transparent"
		}

		GrabPass
		{
			"_BackgroundTexture"
		}

		Pass
		{
			Tags {"LightMode" = "ForwardBase"}

			ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			struct appdata
			{
				float4 pos : POSITION;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 worldPos : TEXCOORD0;
				float3 normal : TEXCOORD1;
			};

			float _RefractionIndex;
			float _Distance;
			float3 _Tint;
			float3 _SpecularColor;
			float _Shiness;
			sampler2D _BackgroundTexture;

			v2f vert (appdata v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.pos);
				o.worldPos = mul(unity_ObjectToWorld, v.pos).xyz;
				o.normal = UnityObjectToWorldNormal(v.normal);
				return o;
			}

			float schlickFresnel(float cosine) {
				float r0 = (1 - _RefractionIndex) / (1 + _RefractionIndex);
				r0 = r0 * r0;
				return r0 + (1 - r0) * pow(1 - cosine, 5);
			}

			fixed4 frag (v2f i) : SV_Target
			{
				float3 normal = normalize(i.normal);
				float3 viewDir = normalize(i.worldPos - _WorldSpaceCameraPos.xyz);

				float3 reflectDir = reflect(viewDir, normal);
				float3 reflectCol = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectDir);

				float3 refractDir = refract(viewDir, normal, 1.0 / _RefractionIndex);
				float3 refractPos = i.worldPos + refractDir * _Distance;
				float4 refractScreenPos = mul(UNITY_MATRIX_VP, float4(refractPos, 1.0));
				float2 screenUv = (refractScreenPos.xy / refractScreenPos.w) * 0.5 + 0.5;
				#if UNITY_UV_STARTS_AT_TOP
				screenUv.y = 1.0 - screenUv.y;
				#endif

				float3 refractCol = tex2D(_BackgroundTexture, screenUv).xyz;
				float3 spec = pow(max(0.0, dot(normalize(_WorldSpaceLightPos0.xyz), reflectDir)), _Shiness) * _SpecularColor * _LightColor0;

				float f = schlickFresnel(max(0.0, dot(-viewDir, normal)));

				float3 c = (1.0 - f) * refractCol * _Tint + f * (reflectCol + spec);
				
				return float4(c, 1.0);
			}
			ENDCG
		}
	}
}
26
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?