接上文:UnityShader20:CommandBuffer初見(上)
四、復雜一點的例子……
CommandBuffer代碼參考如下:
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
[ExecuteInEditMode]
//加上ExecuteInEditMode后,下面的代碼在編輯模式中就會運行,無需每次測驗都要play了
public class CommandBufferBlurRefraction: MonoBehaviour
{
public Shader m_BlurShader;
private Material m_Material;
private Camera m_Cam;
private Dictionary<Camera,CommandBuffer> m_Cameras = new Dictionary<Camera, CommandBuffer>();
private void Cleanup()
{
foreach(var cam in m_Cameras)
{
if (cam.Key)
{
cam.Key.RemoveCommandBuffer(CameraEvent.AfterSkybox, cam.Value);
}
}
m_Cameras.Clear();
Object.DestroyImmediate(m_Material);
}
public void OnEnable()
{
Cleanup();
}
public void OnDisable()
{
Cleanup();
}
public void OnWillRenderObject()
{
var act = gameObject.activeInHierarchy && enabled;
if (!act)
{
Cleanup();
return;
}
var cam = Camera.current;
if (!cam)
return;
CommandBuffer buf = null;
if (m_Cameras.ContainsKey(cam))
return;
if (!m_Material)
{
m_Material = new Material(m_BlurShader);
m_Material.hideFlags = HideFlags.HideAndDontSave;
}
//創建一個CommandBuffer
buf = new CommandBuffer();
buf.name = "Grab screen and blur";
m_Cameras[cam] = buf;
//新建一張紋理,得到對應的紋理ID
int screenCopyID = Shader.PropertyToID("_ScreenCopyTexture");
//初始化這張紋理,其中紋理的大小為攝像機視野大小
buf.GetTemporaryRT(screenCopyID, -1, -1, 0, FilterMode.Bilinear);
//將當前的渲染目標紋理拷貝到你的紋理"_ScreenCopyTexture"上
buf.Blit(BuiltinRenderTextureType.CurrentActive, screenCopyID);
//再新建兩張紋理
int blurredID = Shader.PropertyToID("_Temp1");
int blurredID2 = Shader.PropertyToID("_Temp2");
//初始化這兩張紋理,其中紋理的大小為攝像機視野大小的一半
buf.GetTemporaryRT(blurredID, -2, -2, 0, FilterMode.Bilinear);
buf.GetTemporaryRT(blurredID2, -2, -2, 0, FilterMode.Bilinear);
//開始表演
buf.Blit(screenCopyID, blurredID);
buf.ReleaseTemporaryRT(screenCopyID);
//進行多次的高斯模糊,一次橫向,一次縱向,紋理相互拷貝
buf.SetGlobalVector("offsets", new Vector4(2.0f/Screen.width, 0, 0, 0));
buf.Blit(blurredID, blurredID2, m_Material);
buf.SetGlobalVector("offsets", new Vector4(0, 2.0f/Screen.height, 0, 0));
buf.Blit(blurredID2, blurredID, m_Material);
buf.SetGlobalVector("offsets", new Vector4(4.0f/Screen.width, 0, 0, 0));
buf.Blit(blurredID, blurredID2, m_Material);
buf.SetGlobalVector("offsets", new Vector4(0, 4.0f/Screen.height, 0, 0));
buf.Blit(blurredID2, blurredID, m_Material);
//到這里,紋理blurredID已經就是最終我們要的:進行過高斯模糊的結果,將它賦到對應著色器中的 _RefractionTex 屬性上
buf.SetGlobalTexture("_RefractionTex", blurredID);
cam.AddCommandBuffer(CameraEvent.AfterSkybox, buf);
}
//是的,這整個程序就是為了得到一張你想要的紋理,并傳遞給Shader
}
- CommandBuffer.GetTemporaryRT:獲取一張臨時的 RenderTexture,第1個引數為紋理主鍵,由 Shader.PropertyToID 創建;第2, 3個引數為紋理長寬,傳負數
表示取相機像素的
;第4個引數為深度緩沖位寬(0, 16, 24);第5個引數位紋理過濾模式,這個紋理可以由 ReleaseTemporaryRT 釋放,也會由 Unity 在攝像機完成渲染后自動釋放
-
CommandBuffer.ReleaseTemporaryRT:釋放指定的的臨時渲染紋理
- Shader.PropertyToID:獲得著色器屬性名對應的唯一ID,這個屬性名隨意
- BuiltinRenderTextureType.CurrentActive:獲取當前激活的渲染目標
- CommandBuffer.Blit:將一個紋理復制到另一個紋理,可使用自定義著色器,第1個引數為源紋理 src;第2個引數為目標紋理 target;第三個引數為材質 mat,其內部使用 mat 材質用 src 做 mainTex,clear 為 black 后渲染到 target 上,mat 留空可以理解為直接拷貝紋理,執行這個操作后,target 將會成為新的渲染目標
- CommandBuffer.SetGlobalVector:全域設定著色器向量屬性
-
CommandBuffer.SetGlobalTexture:全域設定著色器紋理屬性
五、更好的玻璃效果(官網案例)
1):著色器代碼部分:這個著色器實作了最簡單的高斯模糊
- 關于高斯模糊:OpenGL基礎51:泛光(文章中的第三節)
Shader "Jaihk662/GaussBlurred1"
{
Properties
{
_MainTex ("Base(RGB)", 2D) = "" {}
}
CGINCLUDE
#include "UnityCG.cginc"
float4 offsets;
sampler2D _MainTex;
/*可以直接用appdata_img代替
struct _2vert
{
float4 vertex: POSITION;
half2 texcoord: TEXCOORD0;
};*/
struct vert2frag
{
float4 pos: SV_POSITION;
float2 uv: TEXCOORD0;
float4 uv01: TEXCOORD1;
float4 uv23: TEXCOORD2;
float4 uv45: TEXCOORD3;
};
vert2frag vert(appdata_img v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy;
//采樣范圍為7x7(周圍3圈),offests的值在.cs中設定
o.uv01 = v.texcoord.xyxy + offsets.xyxy * float4(1, 1, -1, -1);
o.uv23 = v.texcoord.xyxy + offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
o.uv45 = v.texcoord.xyxy + offsets.xyxy * float4(1, 1, -1, -1) * 3.0;
return o;
}
half4 frag(vert2frag i): SV_Target
{
//高斯模糊
half4 color = float4 (0, 0, 0, 0);
color += 0.40 * tex2D (_MainTex, i.uv);
color += 0.15 * tex2D (_MainTex, i.uv01.xy);
color += 0.15 * tex2D (_MainTex, i.uv01.zw);
color += 0.10 * tex2D (_MainTex, i.uv23.xy);
color += 0.10 * tex2D (_MainTex, i.uv23.zw);
color += 0.05 * tex2D (_MainTex, i.uv45.xy);
color += 0.05 * tex2D (_MainTex, i.uv45.zw);
return color;
}
ENDCG
Subshader
{
Pass
{
ZTest Always Cull Off ZWrite Off
Fog { Mode off } //設定霧模式:關閉
CGPROGRAM
//使用低精度(FP16)以提升fragment著色器的運行速度,減少時間
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
CGINCLUDE 關鍵字:和之前常用的 CGPROGRAM 不同,在 CGINCLUDE 和 ENDCG 范圍內插入的 shader 代碼會被插入到所有 Pass 中
- #pragma fragmentoption ARB_precision_hint_fastest:使用低精度(FP16)以提升fragment著色器的運行速度,減少時間
- #pragma fragmentoption ARB_precision_hint_nicest:使用高精度(FP32)
代碼中還設定了 Fog { Mode off }:這是為了關閉霧效,暫時可以不去了解
2):然后對于玻璃本身材質的著色器如下:
Shader "Jaihk662/Glass2"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {} //玻璃材質紋理
_NormalMap ("Normal Map", 2D) = "white" {} //玻璃發現
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {} //模擬環境映射
_Distortion ("Distortion", Range(0, 100)) = 10 //模擬折射時,影像扭曲程度
_RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0 //折射程度,為0只反射,為1只折射
}
SubShader
{
Tags { "Queue" = "Transparent" "RenderType" = "Opaque" }
//Queue設定為Transparent可以保證該物體渲染時,所有的不透明物體都已經被渲染到螢屏上了
LOD 200
PASS
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
sampler2D _NormalMap;
samplerCUBE _Cubemap;
float _Distortion;
fixed _RefractAmount;
sampler2D _RefractionTex; //來自CommandBuffer
float4 _RefractionTex_TexelSize; //對應紋理每一像素的大小
float4 _NormalMap_ST;
float4 _MainTex_ST;
struct _2vert
{
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 tangent: TANGENT;
float2 texcoord: TEXCOORD0;
};
struct vert2frag
{
float4 pos: SV_POSITION;
float4 scrPos: TEXCOORD0;
float4 uv: TEXCOORD1;
float4 TtoW1: TEXCOORD2;
float4 TtoW2: TEXCOORD3;
float4 TtoW3: TEXCOORD4;
};
vert2frag vert(_2vert v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos); //獲得螢屏影像的采樣坐標,考慮過平臺差異,輸入裁剪空間坐標,輸出齊次坐標系下的螢屏坐標值(就是螢屏坐標乘上w,只計算xy,zw不變)
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _NormalMap);
float3 wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 wNormal = UnityObjectToWorldNormal(v.normal);
float3 wTangent = UnityObjectToWorldDir(v.tangent);
float3 wBinormal = cross(wNormal, wTangent) * v.tangent.w;
o.TtoW1 = float4(wTangent.x, wBinormal.x, wNormal.x, wPos.x);
o.TtoW2 = float4(wTangent.y, wBinormal.y, wNormal.y, wPos.y);
o.TtoW3 = float4(wTangent.z, wBinormal.z, wNormal.z, wPos.z);
return o;
}
fixed4 frag(vert2frag i): SV_Target
{
float3 wPos = float3(i.TtoW1.w, i.TtoW2.w, i.TtoW3.w);
fixed3 wViewDir = normalize(UnityWorldSpaceViewDir(wPos));
fixed3 normal = UnpackNormal(tex2D(_NormalMap, i.uv.zw));
//計算折射,考慮玻璃材質
float2 offest = normal.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = i.scrPos.xy + offest * i.scrPos.z;
fixed3 refractCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb; //根據偏移后的坐標進行采樣,得到折射顏色
//不再計算反射
//normal = normalize(half3(dot(i.TtoW1.xyz, normal), dot(i.TtoW2.xyz, normal), dot(i.TtoW3.xyz, normal)));
fixed4 texColor = tex2D(_MainTex, i.uv.xy);
//fixed3 reflectionDir = reflect(-wViewDir, normal);
//fixed3 reflectionCol = texCUBE(_Cubemap, reflectionDir).rgb * texColor.rgb;
fixed3 finalColor = texColor * (1 - _RefractAmount) + refractCol * _RefractAmount;
return fixed4(finalColor, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}
這個代碼就是《UnityShader19.1:渲染紋理(下)之 GrabPass》中代碼的改版,原先 GrabPass 的紋理現在由 CommandBuffer 提供,并且不再計算反射
3):最后 CommandBuffer 部分代碼就是上一章中的代碼
效果如下:

參考資料:
- https://blog.csdn.net/biezhihua/article/details/79141924
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/258812.html
標籤:其他
上一篇:LittleVGL Visual Studio 模擬器的使用
下一篇:萬語千言,不敵一紙情書
