URP 毛玻璃(升级至URP_v14.0.6)
注:这篇笔记的目的不在于如何制作玻璃特效(毛玻璃作为实践的素材),目的在于升级任何使用了【cmd.Blit】这个api的URP特效,以撑持Single-Pass Instanced VR本篇包含内容:
unity版本2021.3.8,URP版本v12.1.7:暂未能撑持Single-Pass Instanced VR;
unity版本2022.2.7,URP版本v14.0.6:可撑持Single-Pass Instanced VR;
(URP在不竭升级,技术更迭…这篇笔记里的东西过不了多久可能也会变成过时的东西,但愿能帮到此刻的你 )
0.一个BRP示例(含毛玻璃shader)
先看一个过时的BRP示例,来自: https://blog.unity.com/technology/extending-unity-5-rendering-pipeline-command-buffers
流程概括:
一个用于做Blur计算的shader(”Hidden/SeparableGlassBlur”);(下面的shader语法已改为URP)
Shader ”Hidden/SeparableGlassBlur” {
Properties {
_MainTex (”Base (RGB)”, 2D) = ”” {}
}
......//注:我喜欢把代码分块排版,这样看起来斗劲舒服,代码里呈现的【......】意思是这部门内容在长虚线下面
}
------------------------------------------------------------------------------------------
Subshader {
Tags { ”RenderPipeline”=”UniversalPipeline” }
Pass {
Name ”01”
Tags { ”LightMode”=”UniversalForward” }
ZTest Always
Cull Off
ZWrite Off
Fog { Mode off }
HLSLPROGRAM
#include ”Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl”
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
......
ENDHLSL
}
}
------------------------------------------------------------------------------------------
struct appdata_img {
float4 vertex : POSITION;
half2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
float4 uv45 : TEXCOORD3;
};
float4 offsets;
sampler2D _MainTex;
v2f vert (appdata_img v) {
v2f o;
o.pos = TransformObjectToHClip(v.vertex);
o.uv.xy = v.uv.xy;
o.uv01 =v.uv.xyxy + offsets.xyxy * float4(1,1, -1,-1);
o.uv23 =v.uv.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0;
o.uv45 =v.uv.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0;
return o;
}
half4 frag (v2f i) : COLOR {
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;
}一个用于衬着玻璃的shader(GlassWithoutGrab.shader);(下面的shader语法已改为URP)
Shader ”FX/Glass/Stained BumpDistort (no grab)” {
Properties {
_BumpAmt(”Distortion”, range (0,64)) = 10
_TintAmt (”Tint Amount”, Range(0,1)) = 0.1
_MainTex (”Tint Color (RGB)”, 2D) = ”white” {}
_BumpMap (”Normalmap”, 2D) = ”bump” {}
}
......
}
------------------------------------------------------------------------------------------
SubShader{
// We must be transparent, so other objects are drawn before this one.
Tags { ”RenderPipeline”=”UniversalPipeline” ”Queue”=”Transparent” ”RenderType”=”Opaque” }
Pass {
Name ”BASE”
Tags { ”LightMode”=”UniversalForward” }
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include ”Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl”
......
ENDHLSL
}
}
------------------------------------------------------------------------------------------
struct appdata_t {
float4 vertex : POSITION;
float2 uv: TEXCOORD0;
};
struct v2f {
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD0;
float2 uvbump : TEXCOORD1;
float2 uvmain : TEXCOORD2;
// UNITY_FOG_COORDS(3)
float fogFactor : TEXCOORD3;
};
float _BumpAmt;
half _TintAmt;
float4 _BumpMap_ST;
float4 _MainTex_ST;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.uvbump = TRANSFORM_TEX( v.uv, _BumpMap );
o.uvmain = TRANSFORM_TEX( v.uv, _MainTex );
o.fogFactor = ComputeFogFactor(o.vertex.z);
return o;
}
sampler2D _GrabBlurTexture;
float4 _GrabBlurTexture_TexelSize;
sampler2D _BumpMap;
sampler2D _MainTex;
half4 frag (v2f i) : SV_Target
{
// calculate perturbed coordinates
// we could optimize this by just reading the x & y without reconstructing the Z
half2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg;
float2 offset = bump * _BumpAmt * _GrabBlurTexture_TexelSize.xy;
i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
half4 col = tex2Dproj (_GrabBlurTexture, i.uvgrab/i.uvgrab.w);
half4 tint = tex2D(_MainTex, i.uvmain);
col = lerp (col, tint, _TintAmt);
col.rgb = MixFog(col.rgb, i.fogFactor);
return col;
}一个担任自Monobehaviour的脚本(CommandBufferBlurRefraction.cs),没有用到RendererFeature;
把 scene color 传输给 RT_screenCopyID;
CommandBufferBlurRefraction.cs脚本:
OnWillRenderObject():
// copy screen into temporary RT
int screenCopyID = Shader.PropertyToID(”_ScreenCopyTexture”);
cmd.GetTemporaryRT (screenCopyID, -1, -1, 0, FilterMode.Bilinear);
cmd.Blit (BuiltinRenderTextureType.CurrentActive, screenCopyID);
// 但我实在不能理解,即使我把上面的cmd.Blit替换为下面这句,毛玻璃效果也正常显示???
// 但如果注释掉cmd.Blit,毛玻璃效果就会消掉,合着意思是只要有cmd.Blit,不管他source给了啥,都传 scene color 给 destination???
cmd.Blit (Shader.PropertyToID(”_ttttttt”), screenCopyID);新建2张RT,长宽是全屏的一半,记为 RT_blurredID 和 RT_blurredID2;把 RT_screenCopyID 传输给 RT_blurredID;
// get two smaller RTs
int blurredID = Shader.PropertyToID(”_Temp1”);
int blurredID2 = Shader.PropertyToID(”_Temp2”);
cmd.GetTemporaryRT (blurredID, -2, -2, 0, FilterMode.Bilinear);
cmd.GetTemporaryRT (blurredID2, -2, -2, 0, FilterMode.Bilinear);
// downsample screen copy into smaller RT, release screen RT
cmd.Blit (screenCopyID, blurredID);
[*]开始倒腾:
[*]把 RT_blurredID 经SeparableGlassBlur.shader计算(偏移”2”在x标的目的加权平均)后传输给 RT_blurredID2;
[*]把 RT_blurredID2 经SeparableGlassBlur.shader计算(偏移”2”在y标的目的加权平均)后传输给 RT_blurredID;
[*]把 RT_blurredID 经SeparableGlassBlur.shader计算(偏移”4”在x标的目的加权平均)后传输给 RT_blurredID2;
[*]把 RT_blurredID2 经SeparableGlassBlur.shader计算(偏移”4”在y标的目的加权平均)后传输给 RT_blurredID;
把 RT_blurredID 传输给 ”_GrabBlurTexture”,传给用于衬着玻璃的shader(GlassWithoutGrab.shader)使用;
cmd.SetGlobalTexture(”_GrabBlurTexture”, blurredID);1. 改用RendererFeature实现
把OnWillRenderObject这个函数里的CommandBuffer改用RendererFeature实现
下面,我们试着把它的脚本(CommandBufferBlurRefraction.cs)改为RendererFeature
参考: https://zhuanlan.zhihu.com/p/248903320
大体照搬上面链接里的脚本,就是记得把 RT_blurredID 传给 ”_GrabBlurTexture”
unity版本2021.3.8,URP版本v12.1.7
代码在这个文件夹里:FrostedGlassRenderFeature.cs
public class FrostedGlassRenderFeature : ScriptableRendererFeature{
//在RendererFeature类里写一个class Settings,并实例化它
public class Settings{
public RenderPassEvent renderEvent = RenderPassEvent.AfterRenderingSkybox;
public Material blurMat;
}
public Settings settings = new Settings();
//----------------------------------------------------------------
public class FrostedGlassRenderPass : ScriptableRenderPass{......}
FrostedGlassRenderPass m_ScriptablePass;
//----------------------------------------------------------------
//在ScriptableRendererFeature的Create方式里实例化ScriptableRenderPass类
public override void Create(){
m_ScriptablePass = new FrostedGlassRenderPass(settings);
}
//----------------------------------------------------------------
//在AddRenderPasses方式里设置ScriptableRenderPass的source
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData){
var src = renderer.cameraColorTarget;
m_ScriptablePass.Setup(src);
//----------------------
renderer.EnqueuePass(m_ScriptablePass);
}
}
------------------------------------------------------------------------------------------
......的部门:
public class FrostedGlassRenderPass : ScriptableRenderPass{
CommandBuffer cmd;
string m_ProfilerTag;//cmd name
Material m_blurMat;
RenderTargetHandle blurredID01;
RenderTargetHandle blurredID02;
RenderTargetIdentifier source;
RenderTargetHandle m_tempColorTex;
//首先写这个ScriptableRenderPass的构造函数
public FrostedGlassRenderPass(FrostedGlassRenderFeature.Settings param){
m_ProfilerTag = ”FrostedGalss”;
this.renderPassEvent = param.renderEvent;
m_blurMat = param.blurMat;
blurredID01.Init(”_Blur01”);
blurredID02.Init(”_Blur02”);
}
//----------------------------------------------------------------
public void Setup(RenderTargetIdentifier src){
this.source = src;//传入renderer.cameraColorTarget
}
//----------------------------------------------------------------
//在Execute方式里执行CommandBuffer
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData){
if ((renderingData.cameraData.camera.cullingMask & 1<<LayerMask.NameToLayer(”UI”))>0) {
//Debug.Log(”camera的cullingMask包含UI层,返回”);//并设置Camera>CullingMask不包罗UI层
return;
}
CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
Vector2[] sizes = {
new Vector2(Screen.width, Screen.height),
new Vector2(Screen.width / 2, Screen.height / 2),
new Vector2(Screen.width / 4, Screen.height / 4),
new Vector2(Screen.width / 8, Screen.height / 8),
};
int numIterations = 3;
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
opaqueDesc.depthBufferBits = 0;
// The precision of the render texture's depth buffer in bits (0, 16, 24 and 32 are supported).// 为什么加这句?
cmd.GetTemporaryRT(m_tempColorTex.id, opaqueDesc, FilterMode.Bilinear);
cmd.Blit(source, m_tempColorTex.Identifier());
for (int i = 0; i < numIterations; ++i){
cmd.GetTemporaryRT(blurredID01.id, opaqueDesc, FilterMode.Bilinear);
cmd.GetTemporaryRT(blurredID02.id, opaqueDesc, FilterMode.Bilinear);
cmd.Blit(m_tempColorTex.Identifier(), blurredID01.Identifier());
cmd.SetGlobalVector(”offsets”, new Vector4(2.0f / sizes.x, 0, 0, 0));
cmd.Blit(blurredID01.Identifier(), blurredID02.Identifier(), m_blurMat);
cmd.SetGlobalVector(”offsets”, new Vector4(0, 2.0f / sizes.y, 0, 0));
cmd.Blit(blurredID02.Identifier(), blurredID01.Identifier(), m_blurMat);
cmd.Blit(blurredID01.Identifier(), m_tempColorTex.Identifier());
}
//把最终的内容传给 ”_GrabBlurTexture”
cmd.SetGlobalTexture(”_GrabBlurTexture”, blurredID01.Identifier());
//-----------
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
//----------------------------------------------------------------
// 释放临时资源
public override void FrameCleanup(CommandBuffer cmd){
cmd.ReleaseTemporaryRT(m_tempColorTex.id);
cmd.ReleaseTemporaryRT(blurredID01.id);
cmd.ReleaseTemporaryRT(blurredID02.id);
}
}附:一个可以开关RendererFeature的脚本,来自<link>
错误谬误:
①是用cmd.Blit实现的传输,不撑持Single-Pass Instanced VR 且URP不建议使用这个过时的API;
②没有使用RTHandle,v13.1.9之后的URP版本需要使用 RTHandle 替代 RenderTargetIdentifier、RenderTargetHandle;
③玻璃后的半透明物体不成见;
④玻璃前的opaque物体也会被衬着到玻璃上,但是不太明显(大部门被玻璃前的opaque物体自身盖住了,只是边缘有点颜色溢出)
下面升级unity版本,可以撑持Single-Pass Instanced VR;
那如果不想升级版本的话,怎么撑持Single-Pass Instanced VR?
呃……我试了一个下午……好吧我不会……
URP v13.1.9之后才有【RenderingUtils.ReAllocateIfNeeded()】方式——用不了RTHandle的话,就用RenderTargetIdentifier;用不了Blitter封装好的方式,那就搬它的源码;可单是源码的话还不够啊,不知道差了什么设置才能撑持Single-Pass Instanced VR;而且不知道为什么在URP版本v12.1.7里,每用1次Blitter源码,画面就会变亮一个等级,为什么会变亮啊……2. 迁移至URP v14.0.6
迁移至unity版本2022.2.7,URP版本v14.0.6
报错:You can only call cameraColorTarget inside the scope of a ScriptableRenderPass. Otherwise the pipeline camera target texture might have not been created or might have already been disposed.
解决方式1:参考<github/UniversalRenderingExamples/RenderPasses>在 RenderPass 的 OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)方式里获取cameraColorTarget
解决方式2:参考<Perform a full screen blit in URP(V14.0.6)>或者参考< Upgrading to version 2022.1 of URP > 在 RenderFeature 里重写SetupRenderPasses()方式,在SetupRenderPasses()方式里你可以访谒renderer.cameraColorTargetHandle
3.升级至 RTHandle & Blitter
为什么要升级?——URP官网说了:cmd.Blit是已过时的API,且不撑持 Single-Pass Instanced VR;在URP项目中,请避免使用CommandBuffer.Blit及其它依赖于它的API(比如RenderingUtils.Blit);详见:URP Blit 入门(这篇笔记还没发)
◆ 用 Blitter 替换cmd.Blit,是否就可以撑持 Single-Pass Instanced VR?是的!shader也要记得升级,见下一章节
◆ 用 RTHandle 替代 RenderTargetHandle(这个obsolete了必定要被替换的)、RenderTargetIdentifier(这个还是可以用的那要替换吗)——在试图用 Blitter 替换 cmd.Blit 的过程中,我找到的解决方式是“不要用RenderTargetIdentifier,全用RTHandle”
◆ 参与了Blit过程的材质球(shader)也需要改削:增加变量用于绑定Blitter函数的 input texture;
详细的踩坑记录见:URP Blit 入门 -03 Class Blitter(这篇笔记还没发)
代码在这个文件夹里:FrostedGlassRenderFeature.cs
(下面这个就是本篇笔记的真心实意了 真是踩了好多坑啊)
public class FrostedGlassRenderFeature : ScriptableRendererFeature{
//在ScriptableRendererFeature类里写一个class Settings,并实例化它
public class Settings{
public RenderPassEvent renderEvent = RenderPassEvent.BeforeRenderingTransparents;
public Material blurMat;
}
public Settings settings = new Settings();
//----------------------------------------------------------------
public class FrostedGlassRenderPass : ScriptableRenderPass{......}
//----------------------------------------------------------------
FrostedGlassRenderPass m_ScriptablePass;
public override void Create(){//在Create方式里实例化ScriptableRenderPass类
m_ScriptablePass = new FrostedGlassRenderPass(settings);//实例化包含了renderPassEvent的设置
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData){
//m_ScriptablePass.Setup(renderer.cameraColorTargetHandle);
//上面这行放在AddRenderPasses()方式里会报错:
//可以放到RenderPass的OnCameraSetup()里;也可以放到RendererFeature的SetupRenderPasses()里;
m_ScriptablePass.ConfigureInput(ScriptableRenderPassInput.Color);
if (renderingData.cameraData.cameraType == CameraType.Game){
renderer.EnqueuePass(m_ScriptablePass);
}
}
}
------------------------------------------------------------------------------------------
......
public class FrostedGlassRenderPass : ScriptableRenderPass{
CommandBuffer cmd;
string m_ProfilerTag;//cmd name
Material m_blurMat;
RTHandle source;
RTHandle rth_tempTex;
RTHandle rth_Blur01;
RTHandle rth_Blur02;
//首先写这个ScriptableRenderPass的构造函数
public FrostedGlassRenderPass(FrostedGlassRenderFeature.Settings param){
m_ProfilerTag = ”FrostedGalss”;
this.renderPassEvent = param.renderEvent;
m_blurMat = param.blurMat;
}
//----------------------------------------------------------------
// This method is called before executing the render pass.
// It can be used to configure render targets and their clear state. Also to create temporary render target textures.
// When empty this render pass will render to the active camera render target.
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) {
var renderer = renderingData.cameraData.renderer;
this.source = renderer.cameraColorTargetHandle;
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
opaqueDesc.depthBufferBits = 0; //为什么加这句?// Color and depth cannot be combined in RTHandles
/*下面这个错误的……就当是记录下黑历史……
// int id_tempTex = Shader.PropertyToID(”_TempTex”);
// cmd.GetTemporaryRT(id_tempTex, opaqueDesc, FilterMode.Bilinear);
// rti_tempTex = new RenderTargetIdentifier(id_tempTex);
// rth_tempTex = RTHandles.Alloc(rti_tempTex);
*/
RenderingUtils.ReAllocateIfNeeded(ref rth_tempTex, opaqueDesc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: ”_TempTex”);
RenderingUtils.ReAllocateIfNeeded(ref rth_Blur01, opaqueDesc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: ”_Blur01”);
RenderingUtils.ReAllocateIfNeeded(ref rth_Blur02, opaqueDesc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: ”_Blur02”);
// rth_tempTex = RTHandles.Alloc(in opaqueDesc,FilterMode.Bilinear,TextureWrapMode.Clamp,name: ”_TempTex”);
// 上面这个也可以正确运行,但是记得要在OnCameraCleanup()里执行Release
}
//----------------------------------------------------------------
// Here you can implement the rendering logic. 在Execute方式里执行CommandBuffer
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData){
CommandBuffer cmd = CommandBufferPool.Get();
using(new ProfilingScope(cmd, new ProfilingSampler(m_ProfilerTag))){
Vector2[] sizes = {
new Vector2(Screen.width, Screen.height),
new Vector2(Screen.width / 2, Screen.height / 2),
new Vector2(Screen.width / 4, Screen.height / 4),
new Vector2(Screen.width / 8, Screen.height / 8),
};
int numIterations = 2;//3
Blitter.BlitCameraTexture(cmd,source,rth_tempTex);//表格里的类型|C|
for (int i = 0; i < numIterations; ++i){
Blitter.BlitCameraTexture(cmd,rth_tempTex,rth_Blur01);//表格里的类型|C|
cmd.SetGlobalVector(”_Offset”, new Vector4(2.0f / sizes.x, 0, 0, 0));
Blitter.BlitCameraTexture(cmd, rth_Blur01, rth_Blur02, m_blurMat, 0);//表格里的类型|A|
cmd.SetGlobalVector(”_Offset”, new Vector4(0, 2.0f / sizes.y, 0, 0));
Blitter.BlitCameraTexture(cmd, rth_Blur02, rth_Blur01, m_blurMat, 0);//表格里的类型|A|
Blitter.BlitCameraTexture(cmd, rth_Blur01, rth_tempTex);//表格里的类型|C|
}
//把最终内容Blit到一个RenderTexture上。
cmd.SetGlobalTexture(”_GrabBlurTexture”, (RenderTargetIdentifier)rth_Blur01);
}
//---------------------------------------------
context.ExecuteCommandBuffer(cmd);// Use ScriptableRenderContext to issue drawing commands or execute command buffers
cmd.Clear();
CommandBufferPool.Release(cmd);
}
//----------------------------------------------------------------
// Cleanup any allocated resources that were created during the execution of this render pass.
public override void OnCameraCleanup(CommandBuffer cmd){}
public override void FrameCleanup(CommandBuffer cmd){}
public void Dispose(){
rth_tempTex?.Release();
rth_Blur01?.Release();
rth_Blur02?.Release();
}
}Shader改削:用于做Blur计算的shader(”Hidden/SeparableGlassBlur”)
使用接受Blitter函数输入的source,不需要手动定义,可直接 include Blit.hlsl;
#include ”Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl”Blit.hlsl可以提供 vertex shader (Vert), input structure (Attributes) and output strucutre (Varyings)
(不贴shader了,直接放下一章节里)
4.如何撑持 Single-Pass Instanced VR?
文档: https://docs.unity3d.com/2022.2/Documentation/Manual/SinglePassInstancing.html
我们已经用 Blitter 替换全部的cmd.Blit了,此刻只需要让shader撑持Single-Pass Instanced VR就大功告成啦
用于做Blur计算的shader:
Shader ”Hidden/SeparableGlassBlur_v2” {
// Properties {}
Subshader {
Tags { ”RenderType”=”Opaque” ”RenderPipeline”=”UniversalPipeline” }
Pass {
Name ”SeparableGlassBlur”
ZTest Always
Cull Off
ZWrite Off
HLSLPROGRAM
#include ”Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl”
// Blit.hlsl 提供 vertex shader (Vert), input structure (Attributes) and output strucutre (Varyings)
#include ”Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl”
#pragma vertex myVert
#pragma fragment myFrag
// 由Blit.hlsl来定义Attributes
// struct Attributes {
//float4 posOS : POSITION;
//half2 uv : TEXCOORD0;
//UNITY_VERTEX_INPUT_INSTANCE_ID //Insert to support Single-Pass Instanced VR
// };
struct v2f {
float4 posCS : SV_POSITION;//POSITION;
float2 uv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
float4 uv45 : TEXCOORD3;
UNITY_VERTEX_OUTPUT_STEREO //Insert to support Single-Pass Instanced VR
};
float4 _Offset;
v2f myVert (Attributes input) {
v2f o;
UNITY_SETUP_INSTANCE_ID(input);//Insert to support Single-Pass Instanced VR
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);//Insert to support Single-Pass Instanced VR
#if SHADER_API_GLES //拷贝自Blit.hlsl
float4 pos = input.positionOS;
float2 uv= input.uv;
#else
float4 pos = GetFullScreenTriangleVertexPosition(input.vertexID);
float2 uv= GetFullScreenTriangleTexCoord(input.vertexID);
#endif
o.posCS = pos;
o.uv = uv * _BlitScaleBias.xy + _BlitScaleBias.zw;
o.uv01 =o.uv.xyxy + _Offset.xyxy * float4(1,1, -1,-1);
o.uv23 =o.uv.xyxy + _Offset.xyxy * float4(1,1, -1,-1) * 2.0;
o.uv45 =o.uv.xyxy + _Offset.xyxy * float4(1,1, -1,-1) * 3.0;
return o;
}
half4 myFrag (v2f i) : SV_Target {//COLOR
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);//Insert to support Single-Pass Instanced VR
half4 color = float4 (0,0,0,0);
color += 0.40 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv);
color += 0.15 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv01.xy);
color += 0.15 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv01.zw);
color += 0.10 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv23.xy);
color += 0.10 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv23.zw);
color += 0.05 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv45.xy);
color += 0.05 * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, i.uv45.zw);
return color;
}
ENDHLSL
}
}
}用于衬着玻璃的shader:
Shader ”FX/Glass/Stained BumpDistort (no grab) v2” {
Properties {
_BumpAmt(”Distortion”, Range (0,256)) = 125
_TintAmt (”Tint Amount”, Range(0,1)) = 0.1
_MainTex (”Tint Color (RGB)”, 2D) = ”white” {}
_BumpMap (”Normalmap”, 2D) = ”bump” {}
}
SubShader{
Tags {”RenderPipeline”=”UniversalPipeline” ”Queue”=”Transparent” ”RenderType”=”Opaque” }
Pass {
Name ”BASE”
Tags { ”LightMode”=”UniversalForward” }
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include ”Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl”
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID //Insert to support Single-Pass Instanced VR
};
struct v2f {
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD0;
float2 uvbump : TEXCOORD1;
float2 uvmain : TEXCOORD2;
float fogFactor : TEXCOORD3;
UNITY_VERTEX_OUTPUT_STEREO //Insert to support Single-Pass Instanced VR
};
float _BumpAmt;
half _TintAmt;
float4 _BumpMap_ST;
float4 _MainTex_ST;
v2f vert (appdata_t v){
v2f o;
UNITY_SETUP_INSTANCE_ID(v); //Insert to support Single-Pass Instanced VR
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //Insert to support Single-Pass Instanced VR
o.vertex = TransformObjectToHClip(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.uvbump = TRANSFORM_TEX( v.texcoord, _BumpMap );
o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );
o.fogFactor = ComputeFogFactor(o.vertex.z);
return o;
}
sampler2D _BumpMap;
sampler2D _MainTex;
float4 _GrabBlurTexture_TexelSize;
TEXTURE2D_X(_GrabBlurTexture);// sampler2D _GrabBlurTexture; //Insert to support Single-Pass Instanced VR
SAMPLER(sampler_GrabBlurTexture);//Insert to support Single-Pass Instanced VR
half4 frag (v2f i) : SV_Target{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);//Insert to support Single-Pass Instanced VR
// calculate perturbed coordinates
// we could optimize this by just reading the x & y without reconstructing the Z
half2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg;
float2 offset = bump * _BumpAmt * _GrabBlurTexture_TexelSize.xy;
i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
// half4 col = tex2Dproj (_GrabBlurTexture, i.uvgrab/i.uvgrab.w);
half4 col = SAMPLE_TEXTURE2D_X (_GrabBlurTexture, sampler_GrabBlurTexture, i.uvgrab.xy/i.uvgrab.w);
half4 tint = tex2D(_MainTex, i.uvmain);
col = lerp (col, tint, _TintAmt);
col.rgb = MixFog(col.rgb, i.fogFactor);
return col;
}
ENDHLSL
}
}
}
完~✿
阿谁……“③玻璃后的半透明物体不成见”啊啊啊啊不想再折腾啦,下次再研究!
5. 完善的毛玻璃效果
Lit+Refraction+Frosted
[*]Lit:偷懒,用ShaderGraph实现了
[*]Refraction:折射是玻璃效果的本质啊!教程参考 Screen Space Refraction for Glass and Water using Shader Graph 来自 < https://www.youtube.com/watch?v=VMsOPUUj0JA>
[*]Frosted:在 shader graph 里 SceneColor节点 是可以撑持single-pass instanced VR的,但如果使用我们自定义的 GrabBlurTexture 就不能撑持single-pass instanced VR了;……才发现我对single-pass instanced VR这个技术其实一点没搞懂
我的解决方式是用 Custom Function 节点,新建一个 hlsl 文件:
//UNITY_SHADER_NO_UPGRADE
#ifndef MYHLSLINCLUDE_INCLUDED
#define MYHLSLINCLUDE_INCLUDED
TEXTURE2D_X(_GrabBlurTexture);// sampler2D _GrabBlurTexture; //Insert to support Single-Pass Instanced VR
SAMPLER(sampler_GrabBlurTexture);//Insert to support Single-Pass Instanced VR
float4 _GrabBlurTexture_TexelSize;
void SampleTextureX_float(float2 uv, out half4 col){
col = SAMPLE_TEXTURE2D_X(_GrabBlurTexture, sampler_GrabBlurTexture, uv);
}
#endif //MYHLSLINCLUDE_INCLUDED唉……
https://www.zhihu.com/video/1670856985879900160
页:
[1]